JWT Decoder:排查 401/403、过期 Token 和签名边界
用 JWT Decoder 排查 API 401/403:读取 exp、nbf、iat、iss、aud、sub、scope 和 role,区分 decode 与 verify,识别 Base64URL、Bearer 复制错误、时钟偏差、权限不匹配、kid/public key 配置和 HMAC/RSA 签名验证边界。
API 返回 401 或 403 时,JWT 往往是第一批要检查的证据。问题可能很简单:token 过期、复制时多了空格、audience 用错;也可能更隐蔽:服务端拿错 public key、时钟有偏差、权限 claim 名称和网关规则不一致。JWT Decoder 的作用,是先把 token 里的 header 和 payload 展开,让你能按字段排查,而不是盲目重新登录或随便换一个 token。
但有一条边界必须先说清楚:decode 不等于 verify。JWT 的 header 和 payload 通常只是 Base64URL 编码,不是加密。任何拿到 token 的人都能读出这两部分。它们可读,不代表可信;真正的可信性来自服务端用预期算法、密钥、issuer 规则和 audience 规则完成验证。
先确认你拿到的是完整 token
真实排查里,很多失败不是 claim 错了,而是复制的 token 本身就不对。标准 JWT 常见形态是三段,用两个点号分隔:
header.payload.signature
排查前先检查这几件事:
- 是否只复制了 token 本体,而不是连同
Bearer前缀一起复制; - 前后是否有空格、换行、引号或逗号;
- 是否因为日志折行导致中间少了一段;
- 是否确实有两个点号分隔出三段;
- token 是否来自当前环境,而不是 dev、staging、prod 混用。
如果 decoder 无法拆出 header 和 payload,先不要分析权限。此时优先判断 token 是否被截断、粘贴错、经过 URL 编码、被日志脱敏,或者根本不是 JWT。有些系统使用 opaque token,看起来也是一长串字符,但它不能像 JWT 一样在本地解出 claims。
读 header:算法、类型和 key id
JWT header 通常很短,但它能告诉你服务端应该怎样验证签名。常见字段包括 alg、typ 和 kid。
{
"alg": "RS256",
"typ": "JWT",
"kid": "2026-rotation-01"
}
排查重点不是“header 写了什么就相信什么”,而是“它是否和服务端配置一致”。如果 header 是 RS256,服务端应该使用对应 issuer 的 public key 验证;如果是 HS256,服务端会使用共享 secret 做 HMAC 验证。把 HMAC secret 当 RSA public key、把 RSA token 用 HMAC 方式验,都会导致签名失败。
kid 常用于密钥轮换。token 明明没过期却突然全部失败时,要检查服务端是否还能拿到这个 kid 对应的 key,缓存是否过期,JWKS 地址是否可访问,网关和应用服务是否使用同一套 issuer 配置。
先看时间字段:exp、nbf、iat
过期 token 是最常见的 401 原因,但时间字段也最容易被误读。JWT 里的时间通常是 NumericDate,也就是 Unix timestamp,单位通常是秒,不是毫秒。
| Claim | 含义 | 排查判断 |
|---|---|---|
exp |
过期时间 | 当前服务端时间是否已经超过它 |
nbf |
not before | token 是否还没到可使用时间 |
iat |
issued at | token 签发时间是否合理,是否来自旧会话 |
看时间时要和服务端时间比,而不是只看你电脑上的本地时间。时区显示、浏览器插件、日志系统和容器时间都可能让人误判。比如本地看起来还有 8 小时,实际是因为你把 UTC 当成本地时间读;或者 iat 比服务器当前时间还晚,说明签发方和验证方时钟不同步。
还要注意秒和毫秒的混淆。如果某个内部服务把毫秒时间戳写进 exp,它可能变成一个远未来时间;如果把秒当毫秒显示,界面上会变成 1970 年附近。decoder 只是显示值,最终应按服务端 JWT 库的解释为准。
401 和 403 要分开查
很多人看到 token 失败就只盯着过期时间。实际上 401 和 403 指向的方向不同:
- 401 更常见于“身份没有通过”:token 缺失、格式错误、签名无效、过期、issuer 不可信;
- 403 更常见于“身份通过但权限不足”:scope 不够、role 不对、tenant 不匹配、用户状态不允许访问。
当然,不同框架的返回码不一定严格,但这个区分能帮助你排查。401 先看 token 结构、时间、issuer、audience、签名配置;403 先看 scope、scp、roles、permissions、tenant、organization、自定义 claim 和后端授权规则。
例如 payload 里有:
{
"iss": "https://auth.example.com/",
"aud": "billing-api",
"sub": "user_42",
"scope": "invoice:read profile:read"
}
如果你正在调用的是 admin-api,aud 可能不对;如果接口需要 invoice:write,当前 scope 也不够。token 没过期并不代表它能访问所有接口。
issuer、audience 和 subject 的常见误配
iss、aud、sub 是排查身份边界的核心字段。
iss 表示签发方。多租户系统、多个身份提供方、旧域名迁移时,最容易出现 issuer 字符串不完全一致的问题。一个结尾斜杠、协议、区域域名或自定义域名不同,都可能导致服务端拒绝。
aud 表示 token 面向哪个资源。登录前端拿到的 ID token,不一定能调用后端 API;给用户资料服务的 access token,也不一定能调用计费服务。排查时要拿 API 文档或网关配置对照,而不是只看 payload 里“有 aud”。
sub 表示主体。它可能是用户 ID、服务账号、客户端 ID,也可能在不同 issuer 下含义不同。权限问题不能只看 sub 存在,还要确认这个主体是否在当前租户、组织或资源范围内有权限。
scope、role 和自定义 claim 不要按名字猜
授权 claim 没有所有系统通用的唯一写法。一个平台可能用 scope 字符串,另一个用 scp 数组;有的把角色放在 roles,有的放在命名空间字段,例如 https://example.com/roles。JWT Decoder 能把这些字段显示出来,但不能告诉你服务端实际读取哪个字段。
排查 403 时建议做一张对照表:
| 接口需要 | token 实际有 | 结论 |
|---|---|---|
invoice:write |
invoice:read |
权限不足 |
tenant acme |
tenant demo |
租户不匹配 |
role admin |
role member |
角色不足 |
audience admin-api |
audience web-client |
token 类型不对 |
不要直接修改 decoded payload 来“补权限”。JWT 的签名覆盖 header 和 payload。你改了任何一个 claim,原 signature 都会失效。正确方式是让 issuer 按正确授权重新签发 token。
Base64URL 和普通 Base64 的边界
JWT 片段使用 Base64URL 编码,它和普通 Base64 相近,但字符集和 padding 处理不同。header 和 payload 是 JSON 编码后的片段,signature 是签名结果。把整段 JWT 放进普通 Base64 Encoder 并期待得到完整 JSON,通常是错误用法。
如果你只想学习结构,可以单独复制第一段或第二段,按 Base64URL 解码后会看到 JSON。但实际排查更建议直接用 JWT 专用 decoder,因为它会按点号拆分、显示 header/payload,并避免你把 signature 当作 JSON 解释。
如果解码后不是 JSON,先检查是否复制错段、token 是否被 URL 编码、是否缺少字符,或者它根本不是 JWT。不要把乱码当作“加密的 payload”。普通 JWT payload 本来就应该可读;如果业务需要不可读,那可能是 JWE 或其他加密格式,不是普通签名 JWT。
签名验证要回到服务端配置
Decoder 可以告诉你 signature 那一段存在,但不能在没有密钥的情况下证明它有效。签名排查通常要看四类配置:
- 算法:HS256、RS256、ES256 等是否和 issuer 一致;
- 密钥:HMAC secret、RSA public key、JWKS 是否正确;
- key id:
kid是否能匹配到当前有效 key; - 验证规则:issuer、audience、clock tolerance、required claims 是否一致。
HMAC 场景下,可以用受控的测试 header、payload 和 secret,通过 HMAC Generator 学习签名输入与输出关系。但不要把生产 secret 粘贴到在线工具。RSA 场景下,要理解 private key 用来签发,public key 用来验证;RSA Encrypt 可以帮助理解非对称密钥概念,但实际 JWT 验签仍应交给后端 JWT 库和 issuer 的 JWKS 配置。
常见签名失败原因包括:密钥轮换后缓存没更新;服务端拿了错误环境的 JWKS;issuer 字符串不匹配;网关和应用服务验证规则不同;把 ID token 当 access token;复制 token 时漏了最后一段。
安全整理排查材料
JWT payload 不是加密内容,里面可能包含用户 ID、邮箱、租户名、角色、组织、内部系统标识和权限范围。排查时不要把完整生产 token 原样贴到聊天、工单或公开 issue。
更安全的做法是:
- 截图或文本只保留必要 claim;
- token 本体替换为
REDACTED_JWT; sub、邮箱、tenant、organization 做示例化;- 保留
exp、aud、iss、scope的形状和问题点; - 如果需要服务端验签,让有权限的人在安全环境里验证,不共享密钥。
如果你要向后端报告问题,最好提供:请求时间、接口路径、返回码、trace id、脱敏后的关键 claims、预期权限、实际错误信息。这样比只说“token 不行”更容易定位。
一套可执行的 JWT 排查顺序
遇到授权失败时,可以按这个顺序处理:
- 确认请求头是否是
Authorization: Bearer <token>,没有多余引号、空格或换行; - 用 JWT Decoder 拆出 header、payload 和 signature;
- 检查
exp、nbf、iat,并和服务端时间对齐; - 对照 API 配置检查
iss和aud; - 对照接口权限检查
scope、role、tenant 和自定义 claim; - 看 header 的
alg、kid是否和服务端验签配置一致; - 如果仍失败,用服务端日志确认是签名失败、claim 校验失败还是授权规则失败;
- claim 不对时重新向 issuer 申请 token,不要手改 payload。
这套流程的核心是分层:先确认 token 完整,再确认时间有效,再确认身份边界,最后确认签名和授权策略。
FAQ
JWT Decoder 能证明 token 是真的吗?
不能。Decoder 只能让 header 和 payload 可读。真实性要靠服务端用正确算法、密钥、issuer 和 audience 规则验证签名。
token 没过期,为什么还是 401?
可能是签名无效、issuer 不匹配、audience 不对、token 被截断、使用了错误环境的 token,或者服务端密钥/JWKS 配置有问题。
401 和 403 应该怎么看?
一般先把 401 当身份验证问题查,把 403 当权限问题查。实际框架可能不严格,但这个思路能帮助你区分“token 无效”和“token 有效但权限不足”。
可以把 decoded payload 改掉再用吗?
不可以。payload 改动后原签名失效。需要不同 claim 或权限时,必须让 issuer 重新签发 token。
JWT payload 是加密的吗?
普通 JWT payload 通常不是加密,只是编码后可读。不要在 payload 中放不该被客户端或持有者看到的敏感信息。