JWT

什么是JWT

Json web token(JWT)是一个含签名并携带用户相关信息的JSON串,页面请求校验登录接口时,请求头中携带JWT串到后端服务,后端通过签名加密串匹配校验,保证信息未被篡改,特别适用于分布式站点的单点登陆(SSO)场景。

传统session认证:

1、用户向服务器发送用户名和密码。
2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。
3、服务器向用户返回一个 session_id,写入用户的 Cookie。
4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。
5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。一种解决方案是 session 数据持久化;另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器,JWT 就是这种方案的一个代表。JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。

JWT的结构

JWT由三个部分组成:header,payload,signature。

Header 部分是一个JSON对象,描述JWT的元数据,alg属性表示签名的算法,默认是HMAC SHA256(写成 HS256);typ属性表示这个令牌的类型,JWT令牌统一写为JWT。

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload 部分也是一个JSON对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用,除了官方字段,还可以在这个部分定义私有字段。

iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号(唯一)

Signature 部分是对前两部分的签名,防止数据篡改。首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,最后生成的JWT为 Base64URL(Header).Base64URL(Payload).Signature。客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。

JWT和token认证方式的区别:两者都是无状态的(服务器不需要存储会话信息),且都不依赖Cookie,使得其可以防止CSRF攻击,也能在禁用Cookie的浏览器环境中正常运行;JWT方式服务器不需要存储任何信息,收到JWT后直接计算签名然后验证,token方式服务器需要存储这个token值,说到底还是需要占用内存。

JWT的缺点

JWT 是一次性的,所以也产生了以下问题:
  1. 无法废弃。JWT的最大优势是服务端不再需要存储Session,使得服务端认证鉴权业务可以方便扩展,避免存储Session所需要引入的Redis等组件,降低了系统架构复杂度。但这也是JWT最大的劣势,由于有效期存储在Token中,JWT Token一旦签发,就会在有效期内一直可用,无法在服务端废止,当用户进行登出操作,只能依赖客户端删除掉本地存储的JWT Token,如果需要禁用用户,单纯使用JWT就无法做到了。

  2. 续签:如果你使用 JWT 做会话管理,传统的 Cookie 续签方案一般都是框架自带的,Session 有效期 30 分钟,30 分钟内如果有访问,有效期被刷新至 30 分钟。一样的道理,要改变 JWT 的有效时间,就要签发新的 JWT。最简单的一种方式是每次请求刷新JWT,即每个 HTTP 请求都返回一个新的 JWT。这个方法不仅暴力不优雅,而且每次请求都要做 JWT 的加密解密,会带来性能问题。另一种方法是在 Redis 中单独为每个 JWT 设置过期时间,每次访问时刷新 JWT 的过期时间。

可以看出想要破解 JWT 一次性的特性,就需要在服务端存储 JWT 的状态。但是引入 redis 之后,就把无状态的 JWT 硬生生变成了有状态了,违背了 JWT 的初衷。而且这个方案和 Session 都差不多了

JWT失效方法

在不借助外力的情况下,让JWT失效的唯一途径就是等token自己过期,无法做到主动让JWT失效。非要让JWT有主动失效的功能只能借助外力,即在服务端存储JWT的状态,在请求时添加判断逻辑,这个与JWT的无状态化、去中心化特性是矛盾的。

1. 白名单方式

认证通过时,把JWT存到Redis中。注销时,从缓存移除JWT。请求资源添加判断JWT在缓存中是否存在,不存在拒绝访问。这种方式和cookie/session机制中的会话失效删除session基本一致。

2. 黑名单方式

注销登录时,缓存JWT至Redis,且缓存有效时间设置为JWT的有效期,请求资源时判断是否存在缓存的黑名单中,存在则拒绝访问。

黑名单不需每次登录都将JWT缓存,仅仅在某些特殊场景下需要缓存JWT,给服务器带来的压力要远远小于白名单的方式,甚至都不需要存储完整的jwt,只需要存储jwt中的唯一id jti即可。黑名单的实现流程:

(1) 认证服务器需要添加一个退出登录接口,在退出登录时将jwt 的唯一id jti添加进redis,并设置有效时间为token的剩余时间。
(2) 所有请求需要经过网关,网关层通过Filter添加校验逻辑,解析并判断用户携带的token jti是否在redis中。若存在,说明该token已失效,拒绝访问;否则放行。

参考:
https://baijiahao.baidu.com/s?id=1729132407246534347&wfr=spider&for=pc
https://blog.csdn.net/WXF_Sir/article/details/122668388
https://blog.csdn.net/weixin_38192427/article/details/115154600

posted @ 2022-07-18 21:44  学海无涯#  阅读(365)  评论(0)    收藏  举报