c#关于JWT跨域身份验证的实现代码
作者:流月无双 发布时间:2023-09-12 18:26:02
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中传输。
来源:https://www.cnblogs.com/dengbo/p/11672495.html


猜你喜欢
- 这几天在项目里面发现我使用@Transactional注解事务之后,抛了异常居然不回滚。后来终于找到了原因。如果你也出现了这种情况,可以从下
- Java jar打包成exe应用程序,可在无JDK/JRE环境下运行老师让做一个小项目,但是需要打包发布出来,因此在网上查了很多资料之后总结
- 图是由一组顶点和一组能够将两个顶点相连的边组成。顶点叫什么名字并不重要,但我们需要一个方法来指代这些顶点。一般使用 0 至 V-1 来表示一
- java弱口令检测机制1. 设计要求应具备检测口令的长度和是否在指定字符集合内的能力。应具备检测口令字符逻辑相邻的能力,如aBc,abC等。
- // 1. 实现一个函数,在一个有序整型数组中二分查找出指定的值,找到则返回该值的位置,找不到返回 -1。package demo;publ
- 本文实例讲述了Android编程之手机壁纸WallPaper设置方法。分享给大家供大家参考,具体如下:/** * Andorid设置手机屏幕
- 前言.NET 生态越来越好,初学的朋友也越来越多。处理同一件简单的问题,随着我们知识的积累解决问题的方法也会越来越多。开始学习一门新的语言,
- 本文实例讲解了统计文本中某个字符串出现的次数或字符串中指定元素出现的次数方法,分享给大家供大家参考,具体内容如下运行效果图:程序查找的上此文
- Java中线程分为两种类型:用户线程和守护(服务)线程。通过Thread.setDaemon(false)设置为用户线程;通过Thread.
- 什么是继承?多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。多个类可以称
- /** * 三角数字: * 比达哥斯拉领导下的古希腊数学家发现了一个有趣的数字序列1, 3, 6, 10, 15, 21,... *
- 后台控制层: public static final String HEAD_IMG_DIR = "D:/upload/&quo
- 程序分析:判断素数的方法:用一个数分别去除2到sqrt(这个数),如果能被整除,则表明此数不是素数,反之是素数。#include <s
- Android RecycleView添加head配置封装的实例这个是把RecycleView的适配器给封装了,直接调用就可以了,还添加了可
- 本文实例讲述了Android编程中自定义dialog用法。分享给大家供大家参考,具体如下:dialog是android中提供的一组弹出提示框
- 汽车前后轮倒车轨迹计算附C#源码(Unity),供大家参考,具体内容如下原理很简单, 都是高中的几何数学部分需要的参数有:车前后轴距;车宽(
- 众所周知Java中的数据类型是强数据类型,基本数据类型之间的转换尤其固定的规则,当数据宽度比较窄的数据类型(如int)转换成数据类型比较宽的
- 本文由老王家组装电脑引出——建造者设计模式,详细介绍建造者模式的基本概念和实现代码,为了便于理解建造
- C#实现的获取路由器MAC地址,路由器外网地址。对于要获取路由器MAC地址,一定需要知道路由器web管理系统的用户名和密码。至于获取路由器的
- 前言值类型和引用类型,是c#比较基础,也必须掌握的知识点,但是也不是那么轻易就能掌握,今天跟着老胡一起来看看吧。 典型类型首先我们