学习 · 指南
10 分钟搞懂 JWT
点号分隔的三段各是什么意思、哪些声明可信,以及为什么解码不等于验签。
三个部分
JWT 看起来像 xxxxx.yyyyy.zzzzz。按点号切开就是三部分:
- Header —— base64url 编码的 JSON,描述算法和类型,例如
{"alg":"HS256","typ":"JWT"}。 - Payload —— base64url 编码的 JSON,承载声明:令牌是发给谁的(
sub)、谁签发的(iss)、什么时候过期(exp)、什么时候签发的(iat)。 - 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,看看不做签名校验时伪造声明有多容易。