什么是JWT?


一、背景

在介绍JWT前,我们先了解一下Cookie、Session和Token,这几个概念我们会比较熟悉,它们是用于Web应用程序中管理用户状态和身份状态的技术。因为在Web应用程序中,Http的通信是无状态的,每个请求都是独立的,所以服务端无法确认当前访问者的身份信息(即每次请求是不是都是同一个人)。

Cookie、Session、Token有什么区别呢?:

  • Cookie:保存在客户端的数据,会在下次请求时携带到Request中进行请求发送到服务端,服务端可以拿Cookie来做身份验证和做定制化处理(每个Cookie会有对应的单一域名,所以会出现跨域问题)
  • Session:保存在服务端的数据,会生成一个sessionId给到客户端存储在Cookie中(也就是说Session要依赖Cookie),客户端下次请求就可以带上sessionId让服务端判断当前是哪个用户
  • Token:不保存在哪一端的数据(除非要做退登删除等操作),由一个加密算法组成,客户端请求时只需要带上请求内容+签名给到服务端即可解析出请求参数+当前用户(签名可以防止篡改请求内容,对应生成的秘钥存在服务端,不可泄露)

从上面可以看到,用Token的方式是更优的,因为既能减少数据存储,也能避免数据被篡改。

二、什么是JWT?

JWT(JSON Web Token)是一种基于Token的认证授权机制,是目前业内流行的跨域认证解决方案。与Session不同的是,我们不需要存储Session信息,只需要通过JWT的加密算法进行生成和校验即可,减少了服务端的压力。

1、JWT组成


JWT其实就是一串字符串,里面包含通过.拆分的三个Base64编码的内容:

  • Header:定义该认证方式为JWT,包括加密算法以及Token类型
  • Payload:需要传递的数据
  • Signature:服务端通过Payload、Header和密钥生成的一个签名(算法用的是Header中的SHA256)

所以JWT一般长这样:${Header}.${Payload}.${Signature},示例如下

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

我们可以在JSON Web Tokens进行解码得到Header、Payload、Signature,如下

2、如何使用JWT?


步骤如下:

  1. 用户通过账号+密码登录成功
  2. 服务端返回JWT给客户端
  3. 客户端后续请求都带上JWT给服务端
  4. 服务端校验JWT合法性和时效性

三、使用JWT要注意什么问题?

使用JWT虽然可以解决跨域认证问题,但是要注意以下问题:

  • 建议客户端将JWT存在localStorage(本地缓存)而不是Cookie中,防止出现CSRF攻击
  • 密钥要保存好,防止泄露出去
  • Payload是明文的,需要避免存放隐私信息
  • JWT尽量加上过期时间防止永久有效(可以在Payload的exp参数设置),当然也不宜过长

四、JWT的优缺点

1、JWT的优点

JWT的优点如下:

  • 无状态:JWT本身就包含了身份验证所需要的所有信息,所以服务端无需存储Session信息
  • 避免CSRF攻击:CSRF(Cross Site Request Forgery)叫做跨站请求伪造,也就是拿我们的身份信息去干坏事(如拿Cookie、Session去把你银行卡的钱转账出去),而JWT可以不依赖Cookie从而避免CSRF攻击
  • 适合移动端应用:传统的Cookie只适合用在Web端,但是JWT也可以用在移动端应用(只需要有地方可以存储JWT即可)
  • 单点登录友好:避免了Cookie可能出现的跨域问题、Session可能出现的多机器不同步Session信息问题,直接将JWT放在客户端就不会有这些问题了

2、JWT的缺点

因为JWT是无状态的,所以如果出现以下行为,单纯靠JWT我们是无法解决这些问题的:

  • 用户被封禁
  • 退出登录
  • 修改密码
  • 用户权限变更
  • 用户被踢下线

在Session中,如果出现这些行为,我们只需要删除服务端对应的Session信息即可,但是如果用JWT的话,在失效之前它都是有效的。

所以我们一般有以下几种方案来解决:

  • 存储JWT:也就是把无状态改为有状态,在Redis/MySQL中存储一份JWT,但是缺点是增加了服务端的压力
  • 黑名单机制:相比存储JWT全量存储,该方案只需要把需要过期的JWT维护,大大减少了数据量
  • 修改密钥:对于不同用户使用不同的密钥,当出现上面行为时,把该用户密钥进行修改即可(缺点是需要额外存储不同用户对应的不同密钥,而且不适用于多端登录退登的问题,会导致一端退登另一端也需要重新登录)
  • 过期时间缩短:跟Redis一样,过期时间缩短了,重建缓存的次数就变多了,就可以让我们更快发现问题。但是会导致JWT无法长时间存储,需要用户经常登录

另外我们如何处理JWT的续签问题呢?主要有以下几种方案:

  • 快过期时更新:类似Session做法,服务端在客户端每次请求时会校验JWT有效期,如果快过期了就会更新新的JWT给客户端更新(缺点是快过期了才会更新)
  • 每次都更新:这种明显服务端压力会比较大,不推荐
  • 双JWT机制:从单JWT改为双JWT(即accessJWT+refreshJWT),当请求时会先携带有效期较短的accessJWT给服务端,如果accessJWT过期了则携带有效期较长的refreshJWT给服务端,然后服务端会返回新的accessJWT给客户端(缺点是实现较复杂,如要保证双JWT同时失效、会出现accessJWT短暂不可用等问题)

五、总结

JWT的优势在于无状态,但是如果我们要实现复杂场景(如上面提到的退登登问题),还是要存储JWT信息的。

当然我们也不是非得用JWT,用UUID、雪花算法等唯一性ID来做也是可以的,但是需要将该唯一标识存储起来(如存Redis中)。


文章作者: GaryLee
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 GaryLee !
  目录