JWT 解码完全指南:读懂 Header、Payload 与签名
联调接口返回 401、前端「登录态莫名失效」,或后端说「Token 不对」时,最省时间的第一步往往不是改业务代码,而是先把 JWT 拆开看清楚:算法是什么、exp 是否已过、sub/roles 是否符合预期。本指南说明 JWT 结构、解码与验签的边界,给出可操作步骤与排错表,并直接对接 WebUtils 本地解码工具。
核心一句话
JWT 的前两段是可解码的声明,不是加密保险箱;签名才负责防篡改。
解码能告诉你「里面写了什么、过没过期」;验签才回答「是否被改过、密钥是否匹配」。两者都做完,再谈业务权限与刷新策略。下面按「结构 → 易混概念 → 操作 → 排错 → 场景 → 选型」展开,可直接在 JWT 解码器 上对照练习。
1. JWT 到底是什么、不是什么
JWT(JSON Web Token,RFC 7519)是一种把 声明(claims) 编码成可传输字符串的格式,常见于登录态、API 授权、服务间调用。标准形态是用两个点号分成三段:
header.payload.signature
| 段 | 内容 | 能否被「看懂」 | 作用 |
|---|---|---|---|
| Header | 类型、算法等元数据 | 是(Base64URL 解码) | 告诉校验方怎么验 |
| Payload | 业务与标准声明 | 是(Base64URL 解码) | 携带身份、过期、权限等 |
| Signature | 对前两段的密码学摘要 | 一般只显示原始串 | 防篡改、绑定密钥 |
不是加密。 Header 与 Payload 只做 Base64URL 编码,任何拿到完整 Token 的人都能用解码器读出 JSON。若把密码、身份证号、银行卡写进 Payload,等于明文外传。需要保密应使用加密通道(HTTPS)与服务端保管的敏感数据,而不是指望「JWT 看起来乱码」。
不是 Session 本身。 JWT 常被当作无状态会话的载体:服务端不存会话表,只校验签名与声明。这带来水平扩展方便,也带来「签发后难即时作废」等问题——吊销、黑名单、短过期 + 刷新令牌是另一层设计,解码工具解决不了策略问题,但能帮你确认当前这枚 Token 里写了什么。
2. 标准声明与你真正该盯的字段
Payload 里常见注册声明(Registered Claims)如下,联调时优先看这些,再看业务自定义字段。
| 声明 | 含义 | 调试时怎么用 |
|---|---|---|
iss |
签发者 | 是否指向正确的认证服务 |
sub |
主体(常为用户 ID) | 是否对上当前登录用户 |
aud |
受众 | 是否发给了正确的 API 客户端 |
exp |
过期时间(Unix 秒) | 401「过期」的第一证据 |
nbf |
生效时间 | 时钟/预发 Token 是否「尚未生效」 |
iat |
签发时间 | 是否异常超前或落后于服务器时间 |
jti |
Token 唯一 ID | 吊销列表、防重放时定位 |
业务字段因项目而异,例如 roles、scope、tenant_id。解码后先确认 类型与层级:数字 1 与字符串 "1"、数组与单值,经常导致「权限看起来有、代码读不到」。
时间字段几乎总是 秒级 Unix 时间戳,不是毫秒。把 exp 当毫秒解析会得到远未来或荒谬日期,误判「没过期」。WebUtils 解码器在解析到 exp/iat 时会附加本地可读时间与是否已过期提示,减少心算错误。
3. Base64、Base64URL、加密:三个最容易混的概念
JWT 使用 Base64URL(RFC 4648):用 -/_ 替代 +//,并常省略 = 填充,方便放进 URL 与 Header。它和普通 Base64 不是同一套字符集。
| 概念 | 做了什么 | 是否保密 | 典型工具 |
|---|---|---|---|
| Base64 / Base64URL | 二进制 ↔ 可打印文本 | 否 | Base64 工具、JWT 解码器内部 |
| 加密(如 AES) | 无密钥难读出明文 | 是(在密钥安全前提下) | 不在「解码 JWT」范围内 |
| 签名(HMAC/RSA 等) | 证明内容未被改、来自持钥方 | 不隐藏 Payload 内容 | 解码器可选 HS256 验证 |
因此:
- 只想「看 Payload」→ 解码即可,不必密钥。
- 要确认「没被改、密钥对」→ 需要验签,且算法与密钥类型要匹配。
- 把整段 JWT 丢进普通 Base64 解码器,常因 URL 安全字符与缺填充而失败;应优先用专用 JWT 解码器。
4. 用 WebUtils 解码:逐步操作
工具页:JWT 解码器。处理在浏览器本地完成;粘贴生产 Token 前仍建议脱敏或使用测试账号令牌。
- 打开工具。 进入 /tools/dev/jwt-decoder,确认「输入 JWT 令牌」文本框可见。
- 准备 Token。 从
Authorization: Bearer …里只复制 Token 本体(三段、两个点),不要带上Bearer前缀或引号、换行。 - 粘贴即解析。 输入框支持
oninput实时解码:成功时 Header / Payload / Signature 三区会分别展示;失败时看状态栏错误提示。 - 先读 Payload。 重点看
exp旁的本地时间与「已过期 / 有效」注释,再看sub、自定义角色字段。 - 再读 Header。 确认
alg(如HS256、RS256)与typ。算法与后端配置不一致时,验签或网关会直接拒绝。 - 可选:HS256 验签。 仅在你 明确持有共享密钥 且 Header 为
HS256时,在「验证签名」框填入密钥。通过显示签名有效;失败则密钥错误、Token 被改,或根本不是该密钥签发。 - 需要时复制 JSON。 各段可用「复制」按钮带走;若要进一步美化或 Diff,可粘到 JSON 格式化。
工具能力边界(对照真实页面)
- 支持: 三段式 JWT 的 Base64URL 解码、Header/Payload 格式化展示、
exp/iat可读时间、HS256 本地验签。 - 不宣称支持: 任意算法全覆盖的验签(页面明确:目前验证侧重 HS256);RSA/ECDSA 公钥验签需其他工具链。
- 不替代: 服务端权威校验、HTTPS、密钥轮换与吊销策略。
5. 常见问题与处理办法
| 现象 | 常见原因 | 怎么处理 |
|---|---|---|
| 提示无效格式(不是三部分) | 复制了 Bearer 前缀、少了点号、粘到两行、只复制了半段 |
只保留 a.b.c 三段;去掉空格与引号 |
| 解析失败 / JSON 错误 | 某段不是合法 Base64URL,或被网关截断 | 从网络面板重新复制完整 Response/Header |
| 显示已过期 | exp 早于当前时间;或客户端时钟不准 |
刷新登录拿新 Token;核对本机时间与 NTP |
| 未过期仍 401 | aud/iss 不匹配、权限 claim 不对、网关另有校验 |
解码确认声明后,再查服务端策略与中间件 |
| HS256 验签失败 | 密钥错、多空格、Token 被改、实际是 RS256 | 核对 alg;RS256 不要用 HMAC 密钥硬验 |
| 验签提示仅支持 HS256 | Header 为 RS256 等 |
解码仍可看内容;验签改用对应公钥工具或后端日志 |
| Payload 里「有字段但前端读不到」 | 嵌套路径、类型不一致、用了错误的解析库 | 复制 Payload 到 JSON 工具核对路径与类型 |
| 签名一改 Payload 就变 | 符合设计:签名绑定前两段 | 改声明必须由签发方重新签名,客户端改无效 |
排错顺序建议
- 格式是否为严格三段。
- 能否解码出 JSON(语法层)。
exp/nbf/iat与时钟(时间层)。iss/aud/sub/角色(声明层)。- 签名与算法(密码学层)。
- 最后才是业务接口与前端状态管理。
多数「登录坏了」在前三层就能定位,不必先怀疑整套权限模型。
6. 真实工作场景
场景 A:接口突然 401
从失败请求的 Request Headers 取出 Bearer Token,贴进解码器。若已过期,走刷新或重新登录;若未过期,对比 aud、路径是否打到错误环境(测试 Token 打生产网关很常见)。把解码后的 exp 本地时间写进工单,比只说「Token 有问题」高效。
场景 B:前后端对「角色」各执一词
解码 Payload,确认角色字段名是 role 还是 roles、是字符串还是数组。用同一枚 Token 在 JSON 视图里对齐,避免在聊天里口头描述结构。必要时把 Payload 贴进 JSON 格式化 再 Diff 两份样例。
场景 C:怀疑 Token 被中间人改过(仍须 HTTPS)
在可控环境用已知 HS256 密钥验签:通过说明内容与密钥一致;失败说明内容或密钥不对。注意:浏览器里粘贴 生产主密钥 有运维风险,优先用测试密钥或仅在本地可信机器操作,用完清理输入框。
场景 D:迁移算法(HS256 → RS256)
先解码确认线上真实 alg,再改网关与签发配置。迁移窗口期常见「旧 Token 仍是 HS256、新服务只认 RS256」——解码一眼能区分,避免只看文档不看实例。
场景 E:移动端 / 小程序存储的长 Token
从日志或调试桥取出完整串(注意日志脱敏)。若解析失败,检查是否被 URL 编码二次处理、是否存进了带换行的 UserDefaults。先解码验证完整性,再查存储层。
场景 F:教学与 Code Review
用伪造的 测试 Token(切勿用真实用户令牌)演示「改 Payload 不改签名必失败」。Review 签发代码时,对照 Header 的 alg 是否硬编码、是否拒绝 alg: none 等危险配置(服务端校验逻辑,解码器只负责观察)。
7. 安全清单:解码前后都要做
- 默认假设 Payload 任何人可读;禁止写入密码、完整支付信息、长期密钥
- 生产 Token 尽量不进聊天群、不进公开 Issue;工单用脱敏或测试账号
- 公共电脑用完清空输入框与剪贴板
- 验签密钥当机密:不要提交到前端仓库,不要写进指南截图
- 传输必须 HTTPS;解码工具不替代传输层安全
- 短过期 + 刷新令牌;仅靠「很长的 exp」换省事会放大泄露窗口
- 服务端必须验签并校验
exp/iss/aud;前端解码只作调试 - 拒绝接受 Header 被篡改为
alg: none或算法混淆攻击(服务端库配置) - 时钟同步:容器与宿主机时间偏差会导致「偶发过期」
9. 和「登录态架构」的边界
解码指南解决的是 观察与排错,不是替你设计整套认证。落地时建议团队约定:
- 文档中示例 Token 一律伪造,并写明算法与过期策略。
- 联调清单增加一步:「401 时先解码再开 Issue」。
- 密钥与公钥轮换有记录;解码器里出现的
kid(若有)能对应到密钥版本。 - 前端可解码展示用户昵称类声明,但 授权决定必须在服务端 重做。
- 监控上区分「签名失败」「过期」「声明不合」三类拒绝,避免全打成统一 401 难排查。
把「能读懂 Token」变成习惯后,身份问题从玄学变成可举证的字段与时间戳。
常见问题
解码 JWT 需要密钥吗?
不需要。Header 与 Payload 只是 Base64URL 编码,解码器即可展示。密钥用于 验证签名(以及加密型 JWT,若使用),与「读取声明」不是同一件事。
为什么我改了 Payload 里的用户 ID,接口还是认旧用户?
因为合法服务端会验签。你改了前两段却没能生成匹配签名,请求应被拒绝;若居然成功,说明服务端 没有正确验签,那是严重安全缺陷,而不是解码器的问题。
HS256 和 RS256 怎么选?解码时有何不同?
HS256 用共享密钥;RS256 用私钥签、公钥验。解码看内容两者相同;本站工具的 验签输入目前面向 HS256。RS256 场景下仍可解码排查过期与声明,验签请用公钥与对应库。
Token 里的 exp 是秒还是毫秒?
JWT 注册声明里的时间一般为 秒。若把秒当毫秒,日期会错得离谱。以解码器给出的本地时间注释为准,并与服务器时间对比。
在线解码安全吗?会不会上传我的 Token?
WebUtils JWT 解码器在浏览器本地解析,不以此为理由把生产高权限令牌随意粘到不可信环境。公共机器、屏幕分享、录屏演示时改用测试 Token,并在结束后清理输入。
只有两段或四段的字符串是 JWT 吗?
标准紧凑序列化是三段。两段可能是其他格式或复制缺失;更多段同样不符合本工具的预期。先恢复完整 header.payload.signature 再解。
解码成功但业务仍失败,下一步看什么?
按层排查:网关是否校验 aud、IP 绑定、是否需要 CSRF/额外 Cookie、刷新令牌是否过期、用户是否被禁用。解码只证明「这枚 Token 内容是什么」,不证明「业务允许你做什么」。
可以把 JWT 当加密手段藏机密吗?
不可以。需要机密应加密存储于服务端或使用专门的加密令牌方案,并始终配合 HTTPS。JWT 默认是 可解码的声明 + 可选签名。
继续浏览
先拿一枚 测试环境 Token 在工具里走通「粘贴 → 看 exp → 可选验签」,再对照错误表处理真实 401。也可返回指南列表或工具目录继续浏览。