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

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

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

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

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

1、JWT的组成跨域

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

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJpc3MiOiLmtYHmnIjml6Dlj4wiLCJleHAiOjE1NzExMDIxNTMsInN1YiI6InRlc3RKV1QiLCJhdWQiOiJVU0VSIiwiaWF0IjoiMjAxOS8xMC8xNSA5OjE1OjQzIiwiZGF0YSI6eyJuYW1lIjoiMTExIiwiYWdlIjoxMSwiYWRkcmVzcyI6Imh1YmVpIn19.
25IbZpAbSXBQsr2k3h0IzKRAC6z3OJTWg38VDtcEER8

 2、和传统session的对比服务器

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

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

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

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

我不认为这是很好的解决方案,原本集群就是为了高可用,若是你配置session的服务器坏了,你们都跟着完蛋,因此JWT这种无状态的方式

就很是适合这种分布式的系统。

 3、代码 JwtHelper

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

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

以下图所示:

 

而后就是生成JWT的方法。

 
 

  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你能够本身传递进来。

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里面的方法,咱们传递参数便可。

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; }

4、调用

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*************************"); } }

结果如图:

 

5、总结

 一、由于json是通用的,因此jwt能够在绝大部分平台能够通用,如java,python,php,.net等

 二、基于jwt是无状态的,jwt能够用于分布式等如今比较流行的一些框架中。

 三、jwt自己不是加密的,因此安全性不是很高,别人知道了你的token就能够解析了,

  固然你本身也能够对jwt进行加密,设置的过时时间不宜过长,同时不要保存一些重要的信息,如密码。

 四、尽可能使用https,这也是为了安全。

 五、JWT字节占用不多,很是的轻便,因此便于传输。

 六、JWT通常放在http的头部Header中传输。

 

---若有错误欢迎指出,你们相互进步。

相关文章
相关标签/搜索