关于 JWT 解码、生成与验签
JWT 解码、生成与验签工具会把令牌拆成头部和载荷,把时间声明渲染成可读时间戳,用你提供的密钥验证签名,也能签出一个用于测试的令牌。解码完全不需要密钥——这是关于这个格式最重要的一件事。
JSON Web Token 是用点连接的三段 Base64URL:声明算法的头部、装着声明的载荷,以及对前两段的签名。前两段是编码而非加密,任何拿到令牌的人都能读出里面的每一个声明。签名能证明的东西很窄但很有价值:签发者持有密钥,且两段内容此后没被改过。至于签发者是不是你期待的那个、受众对不对、是否还在有效期内,都属于你的应用仍然要做的策略校验。
如何使用这个开发者工具
- 选择「解码」查看令牌、「验签」校验签名,或「签名」用一段载荷造一个测试令牌。
- 把令牌粘进左侧面板;签名模式下,这个面板放的是声明 JSON。
- 验签和签名时先选与令牌头部一致的算法,再选密钥格式:HS 系列可选文本、Base64 或十六进制,RS、PS、ES、EdDSA 则用 PEM 或 JWK。
- 把共享密钥或密钥内容粘进面板下方的密钥框。
- 执行操作并阅读结果:解码会额外给出 timeClaims 区块,把 iat、nbf、exp 转换成 ISO 时间戳。
实例:不用密钥也能解码
一个真实的 HS256 令牌。没有提供任何密钥,所有声明照样可读——这正是重点所在。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkRldiBUb29scyIsInJvbGUiOiJlZGl0b3IiLCJpYXQiOjE3NzAwMDAwMDAsImV4cCI6MTc3MDAwMzYwMCwiaXNzIjoiaHR0cHM6Ly9hdXRoLmV4YW1wbGUuY29tIiwiYXVkIjoiYXBpOi8vcmVwb3J0cyJ9.eJqfDF6kCs5lS3B-IoYdxY4V5azCgcxZStnJtWQl3Nc{
"header": {
"alg": "HS256",
"typ": "JWT"
},
"payload": {
"sub": "1234567890",
"name": "Dev Tools",
"role": "editor",
"iat": 1770000000,
"exp": 1770003600,
"iss": "https://auth.example.com",
"aud": "api://reports"
},
"timeClaims": {
"iat": "2026-02-02T02:40:00.000Z",
"exp": "2026-02-02T03:40:00.000Z"
}
}role 声明是在没有任何密钥的情况下读出来的,所以截图或日志里的一个令牌,就等于把它装的东西全都泄露了。timeClaims 还显示这个令牌在签发一小时后就过期了:今天拿去验签会先卡在 exp 时间检查上,签名结果根本轮不到。
注册声明与头部参数
RFC 7519 里的这些声明在规范上是可选的,在工程上是必需的。最后两行是头部参数而不是声明。
| 名称 | 含义 | 类型 | 该怎么校验 |
|---|---|---|---|
| iss | 签发者 | 字符串或 URI | 必须与你配置的签发者完全一致 |
| sub | 主体 | 字符串 | principal,在该签发者范围内唯一且稳定 |
| aud | 受众 | 字符串或数组 | 你的服务必须在其中,否则拒绝 |
| exp | 过期时间 | Unix 秒 | 过了这一刻即拒绝,允许少量时钟偏差 |
| nbf | 生效时间 | Unix 秒 | 早于这一刻即拒绝 |
| iat | 签发时间 | Unix 秒 | 可用于限制令牌年龄和重放窗口 |
| jti | JWT 标识 | 字符串 | 唯一 ID,是做一次性使用追踪的抓手 |
| alg | 算法(头部) | 字符串 | 服务端固定期望值,绝不从令牌里取 |
| kid | 密钥 ID(头部) | 字符串 | 用于从 JWKS 文档中选出对应密钥 |
常见问题排查
令牌能解码,验签却提示签名校验失败。
成因 问题几乎总出在密钥而不是令牌上。以 Base64 或十六进制保存的共享密钥,被当成纯文本输入时得到的是另一串字节;复制时带上的结尾换行同样会改变密钥。
解决办法 把密钥格式选择器调成密钥实际的存储形式:文本、Base64 或十六进制。再核对一下你粘贴的密钥字节长度与签发服务配置的是否一致。
验签报「算法不被允许」。
成因 你选的算法和令牌头部里的 alg 不一致,而验签是刻意只允许你指定的那个算法。
解决办法 先解码令牌读出 header.alg,再选完全相同的算法。在自己的服务里也要把允许的算法固定在服务端——照单全收头部声明的算法,正是 JWT 最经典的漏洞。
别处能用的密钥,在这里验不了 RS256 令牌。
成因 验签需要 SPKI 形式的 PEM 公钥(以 BEGIN PUBLIC KEY 开头)或 JWK。证书、以 BEGIN RSA PUBLIC KEY 开头的 PKCS#1 公钥,以及私钥,都无法作为验签密钥导入。
解决办法 用 openssl x509 -in cert.pem -pubkey -noout 提取公钥;或者从提供方的 JWKS 文档里复制对应的 JWK 对象,并把密钥格式选成 JWK。
身份提供商发的令牌根本解不开。
成因 它不是签名 JWT。五段的令牌是加密的 JWE,而很多提供商签发的是看起来像随机串的不透明引用令牌。另外,从 URL 里复制的值可能还带着百分号编码。
解决办法 数一数点:两个点才是这里能解码的 JWS。带百分号编码的值先用 URL 工具解码。不透明令牌只能通过提供商的 introspection 接口查看。
验签通过了,API 还是拒绝这个请求。
成因 签名有效并不说明签发者、受众、作用域或吊销状态没问题。API 执行的是签名校验覆盖不到的策略。
解决办法 把 iss 和 aud 与 API 的期望值比对,确认所需的 scope 或角色存在,再确认令牌没有在服务端被吊销。
适合用这个工具的场景
- 不用向任何人要密钥,就读出 bug 报告里附带的令牌到底装了什么。
- 把 exp、nbf、iat 转成可读时间,解释会话为什么在那个时刻结束。
- 对接联调时,确认某个公钥或 JWKS 条目确实能验证提供商签发的令牌。
- 生成声明结构符合应用预期的测试令牌,包括故意造一个已过期的。
应该换用别的工具的场景
- 任何涉及生产签名密钥的操作。这类密钥应当留在密钥管理服务里,并在那里完成签名。
- 判断一个令牌是否可信。这需要签发者、受众、作用域和吊销策略,都不在签名校验的覆盖范围内。
- 阅读加密的 JWE 令牌或提供商的不透明令牌,它们都不是签名 JWT,离开提供商就无法解读。
隐私与数据处理
解码、验签和签名都在当前浏览器标签页内通过 Web Crypto API 完成;你粘贴的密钥只存在于页面内存中,离开页面即销毁,全程不上传。但这只是关于传输的陈述,不是关于暴露面的保证:生产环境的签名密钥永远不该离开持有它的系统,能用测试密钥和脱敏令牌时就用它们。
相关开发者工具
常见问题
解码 JWT 等于验证它吗?
不等于。解码只是还原 Base64URL 再解析 JSON,任何人不用密钥都能做。验签则要用密钥和固定的算法重新校验签名,只有到那一步,声明才开始有意义。
既然谁都能读载荷,凭什么他们改不了?
凭签名。头部或载荷改一个字符签名就失效,伪造新签名则必须有密钥。整个安全模型就建立在这上面,这也是绝不能盲目相信 alg 头部的原因。
支持哪些密钥格式?
HS256 / HS384 / HS512 接受文本、Base64 或十六进制形式的共享密钥。RS、PS、ES、EdDSA 系列在签名时用 PEM 私钥、验签时用 PEM 公钥,两个方向也都支持 JWK 对象。
能直接填 JWKS 地址来验签吗?
不能,工具从不发起网络请求。请自己打开提供方的 JWKS 文档,复制 kid 与令牌头部匹配的那一个 JWK 对象,把密钥格式选成 JWK 后粘贴进来。
为什么还能生成 alg 为 none 的令牌?
因为它是个有用的测试用例。alg 为 none 的令牌完全没有签名;任何接受它的服务都存在漏洞,而造一个这样的令牌是验证你的服务不接受它的最快方式。
个人信息可以放进 JWT 吗?
不要放。载荷对任何拿到令牌的人都是可读的,包括浏览器扩展、代理日志,以及任何记录 URL 的环节。声明里只放标识符和授权事实。