JWT 的全称是 JSON Web Token。顾名思义,这是一种 使用 JSON 序列化、用于在网络中安全传递信息的开放标准。

JWS 和 JWE

JWT 只定义了消息以什么格式传递,以及消息应该被保护,没有定义如何保护这些信息。
JWS(JSON Web Signature):JWT 的仅签名不加密的实现,只能防止数据被篡改,不能防止数据被窃取。其实我们现在常说的 JWT 就是指 JWS 实现。
JWE(JSON Web Encryption):JWT 的另一种实现,对数据本身也加密。这个实现并不常用,是因为使用 HTTPS 通信时,JWS 本身就不易被窃取,没有必要再对数据进行一层加密。

下文所说的 JWT 都是指 JWS 实现。

JWT 的结构

JWT 由三部分组成,分别是 头部(Header)、载荷(Payload)、签名(Signature),它们被 '.' 隔开:

Header.Payload.Signature

下面是一个示例(签名使用 xxx... 仅占位):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NzI1MzExOTksInVzZXJfaWQiOjEyMywicm9sZSI6Im5vcm1hbCJ9.xxxxxxxxxxxxxxxxxxxx

Header 和 Payload 都是基于 Base64 编码的 JSON 对象。
你可以到 https://base64.us/ 尝试解码,你将会得到下面两个对象:

Header {"alg":"HS256","typ":"JWT"}
Payload {"exp":1672531199,"user_id":123,"role":"normal"}

Base64 编码是将基于 64 个可打印字符的编码方式,它将字节流每 3 个 byte 一组(共 24 个 bit)拆分成 4 个 6 bit 的字符,映射为可打印字符:A-Z(0-25)、a-z(26-51)、0-9(52-61)、+ / (62 63)。

Header 的格式是固定的。"alg" 字段指定签名算法,HS256 指的是 HMAC_SHA256 加密算法。"typ":"JWT" 表明这是一个 JWT。
Payload 中有些字段是标准声明,也就是由 JWT 公开的规范定义的字段,如 "exp" 表示 JWT 的过期时间。其他字段是私有声明,由用户自行定义。上文中的 "user_id" 和 "role" 就是私有声明。

签名的生成规则如下,例如,使用 HMAC_SHA256 进行签名:

HMAC_SHA256(secret,
base64urlEncoding(header) + "." + base64urlEncoding(payload))

其中 secret 是存储于服务端的不可外泄的密钥
对编码后的 Header 和 Payload 用 '.' 连接而成的字符串进行加密,产生的密文就是签名。
通过对签名的解密和校验,JWT 能确保数据不被篡改。例如,用户不能自行将 "role" 字段改为 "admin"。这也是为什么 JWT 常常用于无状态认证(即用户的状态储存于客户端,发起请求时服务端直接解码,无需从数据库查询)。

能否将密码储存到 JWT?

不能。如上文所述,JWT 中隐私信息很有可能被直接解码获得,将造成非常严重的后果。
同理,设计上也不能使用 JWT 访问敏感数据、账户操作。这也是为什么涉及到隐私信息的查看或者转账时,都需要进行二级身份验证(例如使用手机验证码、邮箱验证等)。

JWT 有哪些缺陷?

  1. 用户无法真正退出登录。JWT 一旦签发,在过期之前都是有效的。
  2. 用户信息无法实时同步。例如,用户的身份更改了,但是之前签发的 JWT 储存的信息还是旧的身份。
  3. Token 泄漏后,没有有效手段立刻将 Token 无效。