跳到正文
ZH
English soon 简体中文 日本語 soon

学习 · 指南

10 分钟搞懂 JWT

点号分隔的三段各是什么意思、哪些声明可信,以及为什么解码不等于验签。

三个部分

JWT 看起来像 xxxxx.yyyyy.zzzzz。按点号切开就是三部分:

  1. Header —— base64url 编码的 JSON,描述算法和类型,例如 {"alg":"HS256","typ":"JWT"}。
  2. Payload —— base64url 编码的 JSON,承载声明:令牌是发给谁的(sub)、谁签发的(iss)、什么时候过期(exp)、什么时候签发的(iat)。
  3. Signature —— 证明 header 和 payload 未被篡改的那串字节,用 header 里指定的算法生成。

因为 header 和 payload 只是编码过的,任何人都能读。所以你绝不要把机密放进 JWT 的 payload。

读声明

最重要的注册声明:

  • exp —— 过期时间,单位是秒的 Unix 时间戳。过了这个时间就拒绝令牌。
  • iat —— 签发时间戳。用来轮换短生命周期的令牌。
  • sub —— 主题(通常是用户 id)。
  • iss/aud —— 签发方与受众;服务端要两者都验证。

陷阱:解码不是验签

把任意令牌粘进解码器就能看到它的声明。这什么都证明不了。攻击者可以自己造一个带 "role":"admin" 的令牌并自行编码,它解码起来毫无破绽。真正拦住它的是你服务端上的签名验证:用共享密钥(HS256)或签发方公钥(RS256)重新计算签名并比对。只有过了这一步才信任这些声明。

该选哪种算法

当第三方需要在不持有你密钥的情况下验证令牌时,用 RS256(非对称);只有你自己的服务签发并验证时,用 HS256(对称)。凡是你没预期的 alg 头,一律拒绝令牌,那句臭名昭著的 alg:none 攻击利用的正是盲目信任 header 的服务端。

用解码器练一下

打开 ToolsKit 的 JWT 解码器,粘一个你自己应用里的真实令牌,读它的 exp 和 iat。然后用 Base64 工具 编码你自己的 header 和 payload,看看不做签名校验时伪造声明有多容易。