分类 默认分类 下的文章

C# webapi实现jwt口令认证入门

jwt因为他的简单易用,并且有一定的加密性,在restful中会广泛使用.
这里记录一下简单的jwt登录验证实现.
通过nuget管理工具安装jwt管理,可以根据自己项目依赖性选择 4.0 3.0或者2.x,本人用的是3.0.0

凡是口令都是两部分,一部分是加密后发送到客户端,一部分是获取客户端加密后的字符串,解密后得到真实值.

using JWT;
using JWT.Algorithms;
using JWT.Serializers;
using Newtonsoft.Json;
....
        // 对象实体类加密
        public static string CreateJWTToken<T>(this T payload, string secret)
        {
            IJwtAlgorithm algorithm = new HMACSHA256Algorithm();
            IJsonSerializer serializer = new JsonNetSerializer();
            IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
            IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);

            string token = encoder.Encode(payload, secret);

            return token;
        }

上面是最简单的加密方式,可以根据自己的需要换不同的加密方式.

        public static string GetJsonByToken(string token, string secret)
        {
            try
            {
                IJsonSerializer serializer = new JsonNetSerializer();
                IDateTimeProvider provider = new UtcDateTimeProvider();
                IJwtValidator validator = new JwtValidator(serializer, provider);
                IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();

                IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder);

                string json = decoder.Decode(token, secret, true);

                return json;
            }
            catch (TokenExpiredException)
            {   //口令过期
                //Console.WriteLine("expired");
                return "expired";
            }
            catch (SignatureVerificationException)
            {   //无效口令.
                //Console.WriteLine("Err");
                return "err";
            }
            catch (Exception)
            {
                throw;
            }
        }

上面是简单的把口令解密成json字符串的方法.不过实际上我们希望得到的一般都是对应的实体类,可以再做一层封装:

        public static T GetObjectByToken<T>(string token, string secret)
        {
            var json = JWTHelper.GetJsonByToken(token, secret);
            if (json == "expired" || json == "err") return default(T);
            try
            {
                T obj = default(T);
                var data = JsonConvert.DeserializeAnonymousType(json, obj);
                return data;
            }
            catch (Exception)
            {
                return default(T);
            }

        }

JsonConvert.DeserializeAnonymousType 这个方法很好用,一般是用来有些json对象只用到一次的时候,单单创建一个匿名对象,而不是重新建一个实体类.

jwt口令加密及解密,就写这么多了,具体如何用到webapi中,可以参考之前写的一篇文章:
AuthorizeAttribute权限配置入门

C#快速解析json技巧

json作为轻量级数据格式,在如今webapi上应用的越来越广了.很多厂商在提供的接口中都用json来输出.

在解析json方面,javascript和python有天生的优势,作为弱类型语言,直接解析了就能当对象用了.在C#里面我们想要解析json,大部分情况下都是先定义一个实体类,然后通过Newtonsoft.Json中的Deserialize方法去反序列化为成这个类对象.

首先引入命名空间using Newtonsoft.Json;

//解析JSON字符串生成对象实体
public static T DeserializeJsonToObject<T>(string json) where T : class
    {
        JsonSerializer serializer = new JsonSerializer();
        StringReader sr = new StringReader(json);
        object o = serializer.Deserialize(new JsonTextReader(sr), typeof(T));
        T t = o as T;
        return t;
    }

那当我们碰到好多个接口,json格式多种多样的时候,就要定义N个类,那岂不是非常麻烦....
后来发现,Newtonsoft.Json的静态对象JsonConvert中给我们提供了反序列化的静态方法:

public static T DeserializeAnonymousType<T>(string value, T anonymousTypeObject);
public static T DeserializeAnonymousType<T>(string value, T anonymousTypeObject, JsonSerializerSettings settings);

那就好办一点了,很多只用到一次的格式我们可以通过定义一个匿名对象,这样可以让我们少定义不少的实体类仅仅只是为了解析json


到了这一步,虽然我们对象定义可以免了,但是对比python和js,还是多了一步定义匿名对象啊,还是比较麻烦不是么?
那我们会说没办法嘛,人家弱类型对象天生就自带光环,天生就是有这优势,本身就不需要实体类,强类型语言怎么跟他们比呢,能有什么办法嘛.
想想也是,可是关键是,我以前用VB编程做网抓的时候,可是吃尽了json的苦头,只能用VBScript去解析json,麻烦的要命.后来发现有老外专门为vb写了一个json解析.真是牛的一B.
附上github链接: https://github.com/greatbody/VB6_JSON_Parse

他里面是把json解析成了两种对象,dictionary对应弱类型里面的对象,collection对应弱类型里面的数组,因为vb里面dictionary和collection的item都可以是object类型,这样就完美的解决了json的解析值存储.
那其实C#完全也能做到哇,C#也可以拿Dictionary和List去存嘛.于是我就本着自己照着人家老外写的一套VB写一套C#用的去网上搜索一下资料.这个时候总算被我找到我想要的了.原来人家Newtonsoft.Json老早就考虑到这点了.(我就想嘛,连我都觉得麻烦人家肯定想到了)
Newtonsoft.Json有封装一个类: Newtonsoft.Json.Linq.JObject 他下面有一个静态方法,不管怎么样的json结构,调用Parse方法,就能得到JObject这个类对象.然后就想怎么用就怎么用了,多的不说了,代码截图放出来大家就都明白了.
ps:我们还可以通过JObject类,像弱类型一样添加属性,最后ToString输出json

        
        string json = "{\"name\":\"BeJson\",\"url\":\"http://www.bejson.com\",\"page\":88,\"isNonProfit\":true,\"address\":{\"street\":\"科技园路.\",\"city\":\"江苏苏州\",\"country\":\"中国\"},\"links\":[{\"name\":\"Google\",\"url\":\"http://www.google.com\"},{\"name\":\"Baidu\",\"url\":\"http://www.baidu.com\"},{\"name\":\"SoSo\",\"url\":\"http://www.SoSo.com\"}]}";
        var jsonObj = Newtonsoft.Json.Linq.JObject.Parse(json);

        string url = jsonObj["url"].ToString();

        var address = jsonObj["address"];
        var street = address["street"].ToString();

JObject使用


注意:如果json最外面是数组格式的话改成Newtonsoft.Json.Linq.JArray.Parse(json);

如果不确定是object或者是array类型,可以用通用的JToken.Parse,只是比前面两个少了对应的部分方法而已

C#程序断言运用

后台接口程序特别是写流程的时候,非常容易出现不易察觉的bug,
很多时候,我们一整天的功能可能就是找出某一个很诡异的bug,最终发现只是改了一句代码而已...

类似的教训告诉我们,平时写代码的时候,要注意暴露错误..
那么断言及抛异常就派上用处了.

C#中断言主要方法:System.Diagnostics.Debug.Assert

一般来讲,对外界传参规范性一般用抛异常去抓取到.
断言则用来规避程序内部流转的变量值有错.特别是调试的时候,如果断言返回false,程序会跳出一个弹窗让你确认,点击重试后,你可以直接跳转到那条语句进行调试.
这个小技巧在平时写代码的时候,特别是循环之类的,可以通过System.Diagnostics.Debug.Assert(i!=8);来很方便的跳转调试

注意:断言处必须尽量写清注释,一般一开始断言都不可能报错,都是后面隐蔽处才会跳出错误.
当在你的程序里存在着各种断言语句时,那么你的程序错误将无处可藏.

倒计时超期图标制作

前端时间美工有出图做一个事件还剩时间的动态图标.
因为页面是在首页,希望可以动态改变剩余的时间,效果如下图:

倒计时图片

这功能的确挺让人眼前一亮的,于是想分享一下当时自己写的框架.
框架是基于canvas去做的.

先给个源码demo链接:
动态倒计时图标

demo效果:

demo中的common.js是一些简单的时间处理函数,其他地方抄来的,我很多地方都会用common.js就不细说了.

框架源码里有几个常量说一下:

  • var CANVAS_WIDTH = 64; 定义绘制图片的canvas的宽高,最终图片会是拉伸状态,最好是和最后生成的图片宽高相同.
  • var FONT_SIZE = 13; 绘制图片上的文字字体大小,这个是和宽高联合调整的,宽高大了,字当然也要相对调整大.
  • var CLOCK_FONT_SIZE = 10; 这个是画时钟里面那个字的大小(本人写的是 "超" 字,时间超了后会显示)
  • var IMAGE_COLOR_ARRAY = [{overValue:,color:},...] 这个数组是定义剩余不同时间,时钟所显示的颜色.要按照overValue降序排列,第一个必然是overValue=null,
    表示默认显示颜色.

程序框架默认剩余分秒状态下会自动更新,超过1天的状态就不更新了,如果有需要也可以把超过1天的状态的也加入更新,更新位置在下面函数过程中,比较简单,就不多做说明.
程序返回一个毫秒数,在setTimeout函数中会调用.

//下一次更新图片的时间.
function nextUpdateTime(timeObject) {
    var obj = timeObject;
    var sDate;
    var num;
    if(obj.day != 0) return 0; //不用更新了.

    if(obj.hour != 0) {
        //未验证,应该没问题,如果有问题改成1分钟刷一次好了
        var s = obj.minute % 3600;
        if(s < 0) s = 3600 + s;
        if(s == 0) {
            s = Math.abs(obj.hour) > 1 ? 3600 : 60;
        }
        num = s * 1000;

    } else if(obj.minute != 0) {

        var s = obj.second % 60;
        if(s < 0) s = 60 + s;
        if(s == 0) {
            s = Math.abs(obj.minute) > 1 ? 60 : 1;
        }
        num = s * 1000;

    } else {
        num = 1000;
    }
    return num;

}

发现什么问题的,欢迎联系作者邮箱^_^