c#关于JWT跨域身份验证的实现代码

所属分类: 软件编程 / C#教程 阅读数: 68
收藏 0 赞 0 分享

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

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

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

一、JWT的组成

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

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJpc3MiOiLmtYHmnIjml6Dlj4wiLCJleHAiOjE1NzExMDIxNTMsInN1YiI6InRlc3RKV1QiLCJhdWQiOiJVU0VSIiwiaWF0IjoiMjAxOS8xMC8xNSA5OjE1OjQzIiwiZGF0YSI6eyJuYW1lIjoiMTExIiwiYWdlIjoxMSwiYWRkcmVzcyI6Imh1YmVpIn19.
25IbZpAbSXBQsr2k3h0IzKRAC6z3OJTWg38VDtcEER8

二、和传统session的对比

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

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

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

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

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

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

三、代码 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;
    }

四、调用

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中传输。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

更多精彩内容其他人还在看

C#获取并修改文件扩展名的方法

这篇文章主要介绍了C#获取并修改文件扩展名的方法,实例分析了C#编程方式修改文件扩展名的技巧,涉及Path类的使用方法,需要的朋友可以参考下
收藏 0 赞 0 分享

C#遍历操作系统下所有驱动器的方法

这篇文章主要介绍了C#遍历操作系统下所有驱动器的方法,涉及C#中DriveInfo类GetDrivers方法的使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C#遍历指定目录下所有文件的方法

这篇文章主要介绍了C#遍历指定目录下所有文件的方法,实例分析了C#中GetFiles()方法遍历文件的使用技巧,非常具有实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C#拷贝文件简单实现方法

这篇文章主要介绍了C#拷贝文件简单实现方法,主要分析了FileInfo类中CopyTo方法针对文件复制的操作技巧,非常简单实用,需要的朋友可以参考下
收藏 0 赞 0 分享

C#获取文件相关信息的方法

这篇文章主要介绍了C#获取文件相关信息的方法,涉及C#中FileInfo类的相关使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C#交错数组用法实例

这篇文章主要介绍了C#交错数组用法,较为详细的分析了交错数组的概念、用法并实例分析了交错数组的使用技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

C#实现在前端网页弹出警告对话框(alert)的方法

这篇文章主要介绍了C#实现在前端网页弹出警告对话框(alert)的方法,涉及C#通过自定义函数调用window.alert方法弹出对话框的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C#实现控制Windows系统关机、重启和注销的方法

这篇文章主要介绍了C#实现控制Windows系统关机、重启和注销的方法,涉及C#调用windows系统命令实现控制开机、关机等操作的技巧,非常简单实用,需要的朋友可以参考下
收藏 0 赞 0 分享

C#读取Excel并转化成XML的方法

这篇文章主要介绍了C#读取Excel并转化成XML的方法,实例分析了C#操作Excel及XML文件的技巧,具有一定参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C#创建一个Word并打开的方法

这篇文章主要介绍了C#创建一个Word并打开的方法,实例分析了C#操作word的常用技巧,非常具有实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享
查看更多