前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >c#关于JWT跨域身份验证解决方案

c#关于JWT跨域身份验证解决方案

作者头像
梁规晓
发布2019-10-22 16:39:07
1.9K0
发布2019-10-22 16:39:07
举报
文章被收录于专栏:DotNet程序园DotNet程序园

学习程序,不是记代码,而是学习一种思想,以及对代码的理解和思考。

JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。为了网络应用环境间传递声明而执行的一种基于JSON的开发标准(RFC 7519),

该token被设计为紧凑且安全的,特别适用于分布式站点的单点登陆(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,

以便于从资源服务器获取资源,该token也可直接被用于认证,也可被加密。

一、JWT的组成

下面是JWT的一段示例,分为三个部分,分别是头部(header),载荷(payload)}和签证(signature),他们之间用点隔开。

代码语言:javascript
复制
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiLmtYHmnIjml6Dlj4wiLCJleHAiOjE1NzExMDIxNTMsInN1YiI6InRlc3RKV1QiLCJhdWQiOiJVU0VSIiwiaWF0IjoiMjAxOS8xMC8xNSA5OjE1OjQzIiwiZGF0YSI6eyJuYW1lIjoiMTExIiwiYWdlIjoxMSwiYWRkcmVzcyI6Imh1YmVpIn19.25IbZpAbSXBQsr2k3h0IzKRAC6z3OJTWg38VDtcEER8
代码语言:javascript
复制

二、和传统session的对比

JWT是基于json的鉴权机制,而且是无状态的,服务器端是没有如传统那样保存客户端的登录信息的,这就为分布式开发提供了便利,

因为传统的方式是在服务端保存session信息,session是保存在内存中的,当客户量变大的时候,对服务器的压力自然会增大,

最关键的是在集群分布式中,每一次登录的服务器可能不一样,那么可能导致session保存在其中一个服务器,而另外一个服务器被请求的

时候还是无状态,除非你再次登录,这就造成了很大的麻烦,也有人说把session存放在专门的服务器,每次都去那个服务器请求,

我不认为这是很好的解决方案,本来集群就是为了高可用,如果你配置session的服务器坏了,大家都跟着完蛋,所以JWT这种无状态的方式

就非常适合这种分布式的系统。

三、代码 JwtHelper

光说不练假把式,下面还是来一段代码。

还是老方式,先用NuGet把JWT引用进来,这里需要引入JWT和newtonsoft.json

如下图所示:

然后就是生成JWT的方法。

代码语言:javascript
复制
  static IJwtAlgorithm algorithm = new HMACSHA256Algorithm();//HMACSHA256加密  static IJsonSerializer serializer = new JsonNetSerializer();//序列化和反序列  static IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();//Base64编解码  static IDateTimeProvider provider = new UtcDateTimeProvider();//UTC时间获取const string secret = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4aKpVo2OHXPwb1R7duLgg";//服务端public static string CreateJWT(Dictionary<string, object> payload){            IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder);            return encoder.Encode(payload, secret);}

看到这段代码,你说觉得怎么这么简单,没错,就是这么简单,这个方法是被我们引用进来的这个JWT给封装了,所以看起来很简单

当时我看到这里也是有点吃惊,不过我还是对源码进行了研究,下面贴出一段源码给大家看看

如下所示,这段代码是比较核心的代码,在没有传递header的时候,他帮你默认加了header,

其实下面的这段代码很容易看懂,无非就是对header和payload进行base64加密(其实这里说加密也不是很恰当)。

这里的header你可以自己传递进来。

代码语言:javascript
复制
public string Encode(IDictionary<string, object> extraHeaders, object payload, byte[] key)        {            if (payload is null)                throw new ArgumentNullException(nameof(payload));
            var segments = new List<string>(3);
            var header = extraHeaders is null ? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase) : new Dictionary<string, object>(extraHeaders, StringComparer.OrdinalIgnoreCase);            header.Add("typ", "JWT");            header.Add("alg", _algorithm.Name);
            var headerBytes = GetBytes(_jsonSerializer.Serialize(header));            var payloadBytes = GetBytes(_jsonSerializer.Serialize(payload));
            segments.Add(_urlEncoder.Encode(headerBytes));            segments.Add(_urlEncoder.Encode(payloadBytes));
            var stringToSign = String.Join(".", segments.ToArray());            var bytesToSign = GetBytes(stringToSign);
            var signature = _algorithm.Sign(key, bytesToSign);            segments.Add(_urlEncoder.Encode(signature));
            return String.Join(".", segments.ToArray());        }

下面一段就是对JWT进行验证的代码,这里的写法都差不多,反正都是调用JWT里面的方法,我们传递参数即可。

代码语言:javascript
复制
public static bool ValidateJWT(string token, out string payload, out string message)        {            bool isValidted = false;            payload = "";            try            {                IJwtValidator validator = new JwtValidator(serializer, provider);//用于验证JWT的类
                IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder);//用于解析JWT的类                payload = decoder.Decode(token, secret, verify: true);
                isValidted = true;
                message = "验证成功";            }            catch (TokenExpiredException)//当前时间大于负载过期时间(负荷中的exp),会引发Token过期异常            {                message = "过期了!";            }            catch (SignatureVerificationException)//如果签名不匹配,引发签名验证异常            {                message = "签名错误!";            }            return isValidted;        }

四、调用

代码语言:javascript
复制
class Program    {        static void Main(string[] args)        {            //载荷(payload)            var payload = new Dictionary<string, object>            {                { "iss","流月无双"},//发行人                { "exp", DateTimeOffset.UtcNow.AddSeconds(10).ToUnixTimeSeconds() },//到期时间                { "sub", "testJWT" }, //主题                { "aud", "USER" }, //用户                { "iat", DateTime.Now.ToString() }, //发布时间                { "data" ,new { name="111",age=11,address="hubei"} }            };            //生成JWT            Console.WriteLine("******************生成JWT*******************");            string  JWTString = JwtHelper.CreateJWT(payload);            Console.WriteLine(JWTString);            Console.WriteLine();
            //校验JWT            Console.WriteLine("*******************校验JWT,获得载荷***************");            string ResultMessage;//需要解析的消息            string Payload;//获取负载            if (JwtHelper.ValidateJWT(JWTString, out Payload, out ResultMessage))            {                Console.WriteLine(Payload);            }            Console.WriteLine(ResultMessage);//验证结果说明            Console.WriteLine("*******************END*************************");        }    }

结果如图:

五、总结

1、因为json是通用的,所以jwt可以在绝大部分平台可以通用,如java,python,php,.net等

2、基于jwt是无状态的,jwt可以用于分布式等现在比较流行的一些框架中。

3、jwt本身不是加密的,所以安全性不是很高,别人知道了你的token就可以解析了,

  当然你自己也可以对jwt进行加密,设置的过期时间不宜过长,同时不要保存一些重要的信息,如密码。

4、尽量使用https,这也是为了安全。

5、JWT字节占用很少,非常的轻便,所以便于传输。

6、JWT一般放在http的头部Header中传输。

---如有错误欢迎指出,大家相互进步。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-10-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet程序园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
多因子身份认证
多因子身份认证(Multi-factor Authentication Service,MFAS)的目的是建立一个多层次的防御体系,通过结合两种或三种认证因子(基于记忆的/基于持有物的/基于生物特征的认证因子)验证访问者的身份,使系统或资源更加安全。攻击者即使破解单一因子(如口令、人脸),应用的安全依然可以得到保障。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档