联调登录接口,后端发的 Token 我这边一用就提示"token 已过期"。 Token 明明是 10 秒前刚拿到的。查了一圈,最后发现是后端那台服务器的系统时间快了 5 分钟 —— Token 的"签发时间"在我这边看来是"5 分钟后",直接被判定异常。
JWT 是现在最主流的鉴权方式,但调试起来坑不少。这篇讲清楚 JWT 的结构, 以及调试时最常踩的 5 个坑。
先搞清 JWT 的结构
一个 JWT 长这样:xxxxx.yyyyy.zzzzz,用点号分成三段:
- Header(头部):Base64URL 编码的 JSON,说明用什么算法(HS256 / RS256)
- Payload(载荷):Base64URL 编码的 JSON,放实际数据(用户 ID / 过期时间等)
- Signature(签名):用密钥对前两段签名,防篡改
关键认知:前两段只是 Base64 编码,不是加密。 任何人拿到 Token 都能解码看到 Header 和 Payload 内容,签名只保证"没被改过"。
坑 1:在 Payload 里放敏感信息
最严重的安全坑。因为 Payload 只是 Base64 编码(不是加密), 任何拿到 Token 的人都能解码看到里面的内容。
绝对不能放进 Payload 的:
- 密码 / 密码 hash
- 身份证号 / 银行卡号
- 手机号(完整的)
- 任何不希望被用户看到的内部数据
可以放的:用户 ID、用户名、角色、过期时间这些"公开了也无所谓"的标识。
坑 2:Token 刚发就过期(时钟不同步)
就是我开头遇到的坑。JWT 的 exp(过期时间)和 iat(签发时间) 都是 Unix 时间戳。如果签发服务器和验证服务器的时钟不同步,Token 一发出来就"已过期"。
怎么排查
- 用 JWT 解析工具看 Payload 里的
iat和exp - 把它们转成可读时间,对比当前时间
- 如果
iat是"未来时间",就是签发服务器时钟快了
怎么解决
- 所有服务器用 NTP 同步时钟
- 验证时设置时钟偏移容忍(leeway,通常 30-60 秒)
坑 3:签名验证失败
签名验证失败的常见原因:
- 密钥不一致:签发和验证用了不同的 secret(最常见,检查环境变量)
- 算法不匹配:签发用 HS256,验证按 RS256
- Token 被截断:Token 放 URL 里没编码,
+/被改 - 复制时混入空格 / 换行:从日志复制 Token 时带了多余字符
排查顺序
- 用 JWT 解析工具看 Header 里的
alg(算法) - 确认验证方用的是同一个算法
- 确认签发和验证用的是同一个密钥
- 检查 Token 字符串有没有被截断 / 混入空白
坑 4:HS256 vs RS256 选错
两种主流算法,适用场景不同:
- HS256(对称):签发和验证用同一个密钥。简单快,适合单体应用
- RS256(非对称):私钥签发、公钥验证。适合分布式 / 微服务 (认证服务持有私钥,业务服务用公钥验证,不用共享私钥)
选错的后果:单体项目用 RS256 徒增复杂度;微服务用 HS256 要在所有服务间共享密钥,泄露风险高。
坑 5:Token 无法撤销 / 续期处理不当
JWT 是无状态的:签发后到期前一直有效,服务端无法直接"撤销"。 这导致两个问题:
问题:用户登出 / 改密码后旧 Token 还能用
解决方案:
- 黑名单:登出的 Token 加入黑名单,验证时检查(牺牲了部分无状态性)
- 短期 access + 长期 refresh:access token 短期(15 分钟),登出让 refresh token 失效
- 版本号:Payload 放 token 版本号,改密码时递增版本号,旧 Token 失效
问题:Token 过期后怎么续期
用双 Token 机制:
- access token:短期(15 分钟-1 小时),用于实际请求
- refresh token:长期(7-30 天),只用于换新 access token
access token 过期时,前端用 refresh token 静默换新的,用户无感知。 refresh token 也过期才要求重新登录。
JWT 调试的标准流程
- 把 Token 粘到解析工具,看 Header / Payload 内容
- 检查 Payload:有没有放敏感信息(安全审计)
- 检查 iat / exp:时间是否合理,转成可读时间对比
- 检查 alg:算法是否和验证方一致
- 检查 Token 完整性:有没有被截断 / 混入空白字符
总结
JWT 调试的 5 个坑:Payload 放敏感信息(安全)、时钟不同步(刚发就过期)、签名验证失败(密钥/算法)、 算法选错(HS256/RS256)、撤销和续期处理不当。记住 JWT 不是加密,Payload 任何人可见。
站里的JWT 解析工具 一键解开 Header / Payload / 签名,自动识别 iat / exp 时间戳并显示过期状态,纯本地不上传 Token。 Token 里的时间戳看不懂,配合时间戳时区那篇理解。