代替Session的JWT

目录

一、为什么不用Session用JWT

二、JWT结构

三、JWT实现登陆的流程

四、ASP.NET Core对于JWT的封装

五、[Authorize]的注意事项

六、解决JWT无法提前撤回的难题


一、为什么不用Session用JWT

          Session在用户登陆验证成功后,服务器端生成唯一标识SessionId,服务器端不仅会把SessionId返回给浏览器端,还会把SessionId和登陆用户的信息的对应关系保存到服务器的内存中;当浏览器端再次向服务器端发送请求的时候,浏览器端就在HTTP请求中携带SessionId,服务器端就可以根据SessionId从服务器的内存中取到用户的信息,这样就实现了用户登陆的功能。

我们一般把SessionId保存在Cookie中,而Session的数据默认是保存在服务器内存中,对于分布式集群环境,Session数据保存在服务器内存中就不合适了,应该保存到一个供所有集群实例访问的共用的状态服务器上。

Session缺点

1、如果Session 数据保存到内存中,当登录用户量很大的时候,Session 数据就会占用非常多的内存,而且无法支持分布式集群环境。
2、如果Session数据保存到 Redis 等状态服务器中,它可以支持分布式集群环境,但是每遇到一次客户端请求都要向状态服务器获取一次 Session 数据,这会导致请求的响应速度变慢。特别是对于一些跨多数据中心的分布式环境,跨数据中心的状态传递更是一件棘手的事情。

在现在的项目开发中,我们倾向于采用JWT代替 Session 实现登录。JWT 是使用JSON格式来保存令牌信息的。JWT 机制不是把用户的登录信息保存在服务器端,而是把登录信息(也叫作令牌)保存在客户端。为了防止客户端的数据造假,保存在客户端的令牌经过了签名处理,而签名的密钥只有服务器端才知道,每次服务器端收到客户端提交的令牌的时候都要检查一下签名,如果发现数据被篡改,则拒绝接收客户端提交的令牌。

 

二、JWT结构

 头部:保存加密算法的说明

负载:保存的是用户的ID、用户名、角色等信息

签名:根据头部和负载一起算出来的值

三、JWT实现登陆的流程

1、客户端向服务器端发送用户名、密码等请求登录。

2、服务器端校验用户名、密码,如果校验成功,则从数据库中取出这个用户的ID、角色等用户相关信息。

3、服务器端采用只有服务器才知道的密钥来对用户信息的JSON字符串进行签名,形成签名数据。

4、服务器端把用户信息的JSON字符串和签名拼接到一起形成JWT,然后发送给客户端。

5、客户端保存服务器端返回的JWT,并且在客户端每次向服务器端发送请求的时候都带上这个JWT。

6、每次服务器端收到浏览器请求中携带的JWT后,服务器端用密钥对JWT的签名进行校验,如果校验成功,服务器端则从JWT中的JSON字符串中读取用户的信息。这样服务器端就知道这个请求对应的用户了,也就实现了登陆的功能。

四、ASP.NET Core对于JWT的封装

第一步:配置系统中配置一个名字叫JWT的节点,在节点下创建SigningKey、ExpireSeconds两个配置项,分别代表JWT的密钥和过期时间。我们再创建一个对应JWT节点的配置类JWTOptions,类中包含SigningKey、ExpireSeconds两个属性。

第二步:NuGet引用程序集Microsoft.AspNetCore.Authentication.JwtBearer包

第三部:编写配置,放到Program.cs的builder.Build之前

 第四步:在Program.cs的app.UseAuthorization之前添加app.UseAuthentication

第五步:在TextController类中增加登陆并且创建JWT的操作方法Login

第六步:在需要登陆才能访问的控制器类上添加[Authorize]这个ASP.NET Core内置的Attribute

五、[Authorize]的注意事项

       [Authorizel这个Attribute既可以被添加到控制器类上,也可以被添加到操作方法上。我们可以在控制器类上标注[Authorize],那么这个控制器类中的所有操作方法都会被进行身份验证和授权验证:对于标注了[Authorize]的控制器类,如果其中某个操作方法不想被验证,我们可以在这个操作方法上添加[AllowAnonymous]。如果没有在控制器类上标注[Authorize],那么这个控制器类中的所有操作方法都允许被自由地访问:对于没有标注[Authorize]的控制器类,如果其中某个操作方法需要被验证,我们也可以在操作方法上添加[Authorize]。

六、解决JWT无法提前撤回的难题

        JWT 把用户信息保存到客户端,而不像 Session 那样在服务器端保存状态,因此更加适合分布式系统及前后端分离项目,但是任何技术都不是完美的,JWT 的缺点是:一旦JWT 被发放给客户端,在有效期内这个令牌就一直有效,令牌是无法被提前撤回的。哪些场景会需要在JWT过期之间提前撤回令牌呢?比如,用户被删除了,那么针对这个用户的令牌就要被撤回,否则会出现客户端使用已经被删除的用户身份的问题:再如,某个JWT 被恶意攻击者拿到并用来发送恶意请求,我们也要撤回针对这个用户的令牌,以便阻断攻击者: 再如,用户在 A设备上登录了,稍后又在 B 设备上登录了,我们就需要把用户在 A 设备上登录获得的JWT 撤回,否则就会出现用户同时在多个设备上登录的问题。上面提到的这些需求其实用传统的 Session 实现更合适

思路:在用户表中增加一个整数类型的列 JWTVersion,它代表最后次发放出去的今牌的版本号:每次登录、发放令牌的时候,我们都让JWTVersion 的值自增同时将JWTVersion 的值也放到JWT 的负载中:当执行用用户、撤回用户的令牌等操作的时候,我们让这个用户对应的JWTVersion的值自增:当服务器端收到客户端提交的JWT 后,把JWT 中的JWTVersion 值和数据中的JWTVersion 值做比较,如果JWT中JWTVersion的值小于数据库中JWTVersion 的值,就说明这个JWT 过期了,这样我们就实现了JWT 的撤回机制。由于我们在用户表中保存了JWTVersion 值,因此这种方案本质上仍然是在服务器端保存状态,这是绕不过去的,只不过这种方案是一种缺点比较少的妥协方案。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咬口大葱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值