JWT的学习

一.代替Session(会话)的IWT

  1. Session的用法

我们知道,HTTP 是无状态的,因此如果要实现“用户登录后才能访问某些资源”的功能开发人员就要自己基于 HTTP来模拟实现状态的保存。实现用户登录功能的经典做法是用 Session,也就是在用户登录验证成功后,服务器端生成唯一标识 SessionId,服务器端不仅把 SessionId 返回给浏览器端,还会把 SessionId 和登录用户的信息的对应关系保存到服务器内存中;当浏览器端再次向服务器端发送请求的时候,浏览器端就在 HTTP 请求中携带 essionId,服务器端就可以根据 SessionId 从服务器的内存中取到用户的信息,这样就实现了用户登录的功能。

我们一般把 SessionId 保存在Cookie 中,而Session的数据默认是保存在服务器内存中的对于分布式集群环境,Session 数存在服务器内存中就不合适了,应该保存到一个供所集群实例访问的共用的状态服务器上。ASPNET Core Session机制,而且我们也采用Redis、Memcached、关系数据库等作为状态服务器,以便支持分布式集群环境。

Session 是Web开发中在服务器端保存客户端相关状态的经典方案,但是在分布式环境下特别是在“前后端分离、多客户端”时代,Session 暴露出很多缺点。这些缺点包括但不局限于如下几点。

第一点.如果 Session 数据保存到内存中,当登录用户量很大的时候,Session 数据就会占用非常多的内存,而且无法支持分布式集群环境。

第二点 如果 Session 数据保存到 Redis 等状态服务器中,它可以支持分布式集群环境,但是

每遇到一次客户端请求都要向状态服务器获取一次 Session 数据,这会导致请求的响应速度变慢。特别是对于一些跨多数据中心的分布式环境,跨数据中心的状态传递更是一件棘手的事情。所以接下来我们引入JWT的概念和用法。

  1. JWT的概念及用法

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

JWT的结构图

编辑

JWT 的头部(header)中保存的是加密算法的说明,负载(payload)中保存的是用户 ID、用户名、角色等信息,签名(signature)是根据头部和负载一起算出来的值。

JWT 实现登录的流程如下。

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

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

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

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

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

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

3. JWT的优点

(1)状态保存在客户端,而非服务器端。天然适合分布式系统。

(2)签名保证了客户端无法数据造假。

(3)性能更高,不需要和中心状态服务器通讯,纯内存的计算。

二.JWT的使用

  1. 创建一个程序用来创建JWT,在创建一个程序读取JWT。.NET进行读写的安装包为System.IdentityModel.Token.Jwt,因此2个程序中都要下载安装包。

  1. 生成JWT令牌

代码解释:在.NET中Claim就代表一条用户信息。Claim有2个主要属性:Type和Value,他们都是String类型的,Type代表用户信息的类型,Value代表用户信息的值,然后Type是String类型的,因此可以取任何值。

  1. 运行的结果为

运行结果虽然看着很乱但其实是明文的,有三部组成,头部、负载和签名。那么接下来通过JWT代码解密一下字符串。

  1. 在JWT文件下在新建一个JWT读取项目

解密时有3个特殊字符+,=,/其中把=删掉,把+换成-,把/号换成_。

代码分析:第1行把字符串加进来

第2行:把三部分依次取出来

第3行:依次取出来的头部

第4行:依次取出来的负载

第5行:依次取出来的签名

第6-8行:用JwtDecode方法把三部分依次解密出来

第二页的代码是固定的是公开的算法

运行结果

由此得出的结论:JWT中的负载存储的内容是明文形式存储的,因此不希望客户端知道的信息不能存储到JWT中。

三.我们知道,JWT 的编码和解码规则都是公开的,而且负载部分的 Claim 信息也是明文的,因此恶意攻击者可以对负载部分中的用户ID等信息进行修改,从而冒充其他用户的身份来访问服务器上的资源。因此,服务器端需要对签名部分进行校验,从而检查 JWT 是否被篡改了。我们可以调用JwtSecurityTokenHandler 类对 JWT 进行解码,因为它会在对 JWT解码前签名进行校验。

  1. 在JWT中新进一个控制台项目命名为JWT读取程序校验签名

  1. 装一下System.IdentityModel.Token.Jwt安装包

代码分析:

上面第6行代码中,采用的是和生成 JWT 时一样的密钥;第 14、105行代码中,我们调用

ValidateToken 方法对JWT 进行解密,ValidateToken 方法的返回值是ClaimsPrincipal类型的, ClaimsPrincipal类型的 Claims 属性就是解析出来的所有的Claim。

猜你喜欢

转载自blog.csdn.net/qq_71012549/article/details/128584096
jwt