JWT 结构:解码与校验
理解 JSON Web Token 的三段结构、解码能说明什么,以及调试身份认证时应关注哪些声明。
三段内容,两个点
JWT 的形式是 header.payload.signature:三个由点连接的 Base64URL 编码片段。头部声明签名算法(alg)和令牌类型,载荷包含声明,也就是实际数据。签名是针对前两个片段计算出的密码学证明,用来证明令牌由持有密钥的一方签发且没有被修改。
三个片段都只是编码,不是加密。任何看到令牌的人都能读取载荷,因此绝不要把密码、秘密或个人数据放进 JWT 声明。
解码不等于校验
解码 JWT(调试器所做的事情)只是对片段进行 Base64 解码并美化显示 JSON。它能回答“这个令牌声称了什么”,却不能回答“这个令牌真实吗”。校验需要签名密钥(非对称算法则使用公钥,例如 RS256/EdDSA),并重新计算签名。
这是 JWT 最重要的安全事实。客户端解码可以安全地帮助你查看过期时间或角色;但在服务端不校验签名就信任解码后的声明,是大量身份认证绕过漏洞的根源。
调试时要检查的声明
- iss——签发者:谁创建了令牌。签发者错误,通常说明令牌来自另一个环境。
- sub——主题:令牌所代表的用户或主体。
- aud——受众:令牌是发给哪个服务使用的。aud 不匹配是常见的 401 原因。
- exp——过期时间,以 Unix 秒表示。把它转换出来,与当前时钟比较。
- iat / nbf——签发时间和生效时间。来自未来的令牌(时钟偏差)与过期令牌一样会校验失败。
- scope / roles——授权细节,完全由应用自行定义。
常见失败模式
令牌过期(exp 已在过去)是最常见的情况:应将 exp 与实际当前时间比较,而不是凭感觉判断。签发者和校验器之间的时钟偏差会破坏 nbf 和 iat 检查;多数库允许少量误差。算法混淆——服务端接受 alg=none,或信任头部给出的算法而不强制使用预期算法——是真实攻击,不只是普通 bug。现代库通常会禁止它,但自定义校验代码仍可能犯错。
最后要记住:JWT 在过期前都有效,没有内置的撤销机制。较短的 exp 加刷新令牌是标准缓解方式,拒绝列表是最后的补救手段。
参考资料
相关工具
- JWT 解码器在本地解码 JWT 的头部和载荷,不进行签名校验。
- Base64 编解码器在浏览器本地编码和解码 Base64 与 Base64URL。
- Unix 时间戳转换器在 Unix 时间戳与可读日期之间互相转换。