什么是JWT

JWT (JSON Web Token) 是目前最流行的跨域认证解决方案,是一种基于 Token 的认证授权机制。从 JWT 的全称可以看出,JWT 本身也是 Token,一种规范化之后的 JSON 结构的 Token。

Token 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。

并且, 使用 Token 认证可以有效避免 CSRF 攻击,因为 Token 一般是存在在 localStorage 中,使用 JWT 进行身份验证的过程中是不会涉及到 Cookie 的。

JWT由哪些部分组成

JWT通过「.」切分成三个为Base64编码的部分

  • Header : 描述 JWT 的元数据,定义了生成签名的算法以及 Token 的类型。
  • Payload : 用来存放实际需要传递的数据
  • Signature(签名) :服务器通过 Payload、Header 和一个密钥 (Secret) 使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成

Header和Payload都是JSON格式的数据,默认不加密。

Header通常由两部分组成

  • typ「Type」:令牌类型,也就成JWT
  • alg「Algorithm」:签名算法,比如HS256

Payload

包含了Claims(声明,包含JWT的相关信息)。Claims可以自定义,也就是说可以往里面放我们想要加入的东西。

  • iss:JWT签发方
  • iat:签发时间
  • exp:过期时间
  • ……

Signature

这部分是对前两部分的签名,作用是防止Token(主要是Payload)被人为篡改。

这个签名的生成需要:

  • Header+Payload
  • 存放在服务器的密钥「一定不要泄漏」
  • 签名算法

如何基于JWT进行身份验证

  1. 用户向服务器发送用户名、密码以及验证码用于登陆系统。
  2. 如果用户用户名、密码以及验证码校验正确的话,服务端会返回已经签名的 Token
  3. 用户以后每次向后端发请求都在 Header 中带上这个 Token
    1. 建议将Token放在localStorage中,放在Cookie中会有CSRF风险
    2. 常见做法是将Token放在HTTP请求头的Authorization字段中「Authorization: Bearer Token」
  4. 服务端检查 Token 并从中获取用户相关信息。

JWT如何防止Token被篡改

JWT安全的核心在Signature,Signature安全的核心在密钥。一定不要让密钥泄漏出去。

服务端拿到 Token 之后,会解析出其中包含的 Header、Payload 以及 Signature 。服务端会根据 Header、Payload、密钥再次生成一个 Signature。拿新生成的 Signature 和 Token 中的 Signature 作对比,如果一样就说明 Header 和 Payload 没有被修改。

JWT的优势

无状态

JWT 自身包含了身份验证所需要的所有信息,因此,我们的服务器不需要存储 Session 信息。这显然增加了系统的可用性和伸缩性,大大减轻了服务端的压力。

有效避免了CSRF攻击

CSRF攻击指跨站请求伪造,这个黑客攻击依赖于Cookie。JWT一般存放在LocalStorage中,整个过程压根不会涉及到Cookie。

适合移动端应用

同样,若使用Session进行身份认证需要依赖Cookie,不适合移动端。

解决注销登录等场景下JWT还有效

与之类似的相关场景还有:

  • 退出登录;
  • 修改密码;
  • 服务端修改了某个用户具有的权限或者角色;
  • 用户的帐户被封禁 / 删除;
  • 用户被服务端强制注销;
  • 用户被踢下线;

1. 将JWT存入内存数据库

如果需要让某个JWT失效就直接从Redis中删除这个JWT即可。但是,这样会导致每次使用 JWT 发送请求都要先从 DB 中查询 JWT 是否存在的步骤,而且违背了 JWT 的无状态原则。

2. 黑名单机制

和方案1类似。如果想让某个JWT失效的话就直接讲这个JWT加入到黑名单中。然后每次请求前,判断下JWT是否存在于黑名单中。

JWT的续签问题

JWT 有效期一般都建议设置的不太长,那么 JWT 过期后如何认证,如何实现动态刷新 JWT,避免用户经常需要重新登录。

1. 每次请求都返回新JWT

思路简单,但开销大。特别是服务端要存储维护JWT的情况下

2. 类似Session

每次都校验一下。如果快过期了,服务器就重新生成JWT给客户端。客户端若发现JWT不一致,则更新LocalStorage的JWT。这种做法的问题是仅仅在快过期的时候请求才会更新 JWT , 对客户端不是很友好。

3. JWT有效期设置到半夜

或许这根本不能算解决方式

4. 设置两个JWT

一个是过期时间较短的accessJWT,一个是过期时间更长的refereshJWT。

客户端登录后,将 accessJWT 和 refreshJWT 保存在本地,每次访问将 accessJWT 传给服务端。服务端校验 accessJWT 的有效性,如果过期的话,就将 refreshJWT 传给服务端。如果有效,服务端就生成新的 accessJWT 给客户端。否则,客户端就重新登录即可。