JWT 解析出来的是什么?三段结构、时间声明与「解析不等于验签」
JWT 解析,就是把这串用点分隔的文本还原成两段 JSON 加一段签名。这一过程不需要密钥,所以任何拿到字符串的人都能解开——这既是它方便的地方,也是它容易被误读的地方:能解析不代表 token 有效。
宝塔的 JWT 解析工具 做的是第一步:把 Header、Payload、Signature 三段分开显示,把标准声明的名字翻成人能读的说明,并顺手把时间声明换算成本地时间。下面所有内容都来自 2026-11-06 的一次真实运行,正文数字与截图同源。
三段分别是什么:两个 JSON 加一段签名
按 RFC 7519 与 RFC 7515 的定义,JWT 的紧凑格式是 base64url(header) + "." + base64url(payload) + "." + base64url(signature),三段之间用点分隔,缺一段就不是完整的 JWT。
拿一段 HS256 的 token 试:header 是 {"alg":"HS256","typ":"JWT"},payload 里放了 sub、name、role、iss、aud、jti、iat、exp 八个声明。粘贴进工具后:
- Header 框原样还原出
{"alg":"HS256","typ":"JWT"}; - Payload 框原样还原出那段 JSON,键的顺序和原始字符串一致,不做排序也不美化;
- Signature 框只给出 base64url 字符串本身,例如
BU5ezjXOvprC5e7Zri5Na02BMeV7L8EjoPAZ8deHsn8,不做解码——签名是二进制结果的编码,本来就不是 JSON。

表格第三列「说明」是哪来的:标准声明的固定含义
工具把 header 和 payload 各渲染成一张三列表:键、值、说明。「说明」列不是宝塔自造的词,而是标准里给这些名字定的含义——RFC 7519 第 4.1 节的 Registered Claim Names,以及 OpenID Connect 里常用的几个。
实测这段 payload 的对照结果:
| 键 | 值 | 说明 |
|---|---|---|
| sub | u_9527 | Subject |
| name | 张三 | Full name |
| role | admin | (空,非标准声明) |
| iss | t.bt.cn | Issuer |
| aud | api.bt.cn | Audience |
| jti | a1b2c3 | JWT ID |
| iat | 1793947008 | Issued At |
| exp | 1793954328 | Expiration Time |
两个细节值得注意:
- 非标准声明不编说明。
role是自定义键,说明列就是空的,工具不会硬套一个含义。 - 数组和对象按 JSON 直出。把
aud写成数组["api.bt.cn","admin.bt.cn"]、再加一个嵌套对象声明,表格里显示的就是["api.bt.cn","admin.bt.cn"]和{"ip":"1.2.3.4","ua":"curl"}这样的 JSON 文本,不会变成[object Object]。
header 的 alg 也一样:值下面用绿色补一行算法全称。实测 HS256 显示 HMAC using SHA-256,RS256 显示 RSASSA-PKCS1-v1_5 using SHA-256,ES256 显示 ECDSA using P-256 and SHA-256,alg 填 none 时显示 No digital signature or MAC performed;这些写法就是 RFC 7518 里的算法名。
时间为什么显示成 11/6/2026 2:36:48 PM
payload 里的 iat、exp、nbf 是 NumericDate,也就是从 1970-01-01T00:00:00Z 起算的 UTC 秒数。工具在原始数字下面补一行换算结果:转成浏览器所在时区,格式是 M/D/YYYY h:mm:ss AM/PM。
截图里 iat 是 1793947008,显示 11/6/2026 2:36:48 PM;exp 是 1793954328,显示 11/6/2026 4:38:48 PM。两个数字差 7320 秒,换算后正好差 2 小时 2 分(4:38:48 减 2:36:48)。
所以「时间对不上」通常不是 token 坏了,而是三件事之一:
- 浏览器时区不是你预期的时区(这个格式里没有时区标记,看的人容易当成 UTC);
- 服务器时钟偏了,签发时写进去的秒数本身就错;
- 把
11/6/2026读成了别的日期——这是美式月/日/年顺序,不是 ISO 8601 的年月日。
解析成功 ≠ token 有效:签名和过期都不在这一步校验
这是最容易踩的一条。两个实测:
- 把一段 RS256 token 的签名段换成
Zm9vYmFy(随便一段合法 base64url),工具照常显示 header 表和 payload 表,没有任何报错; - 用一段 exp 比当前时间早一天的 token,表格照常渲染,页面上没有任何「已过期」提示。
原因在结构上:验证 HS256 要用密钥重算一次 HMAC 再比对,验证 RS256 要用公钥验签,两者都必须有密钥;而解析器只做 base64url 解码,手里没有任何密钥。所以「解析正常」只能说明格式没坏,说明不了签名没被改、也说明不了没过期。
要真正验签,得在服务端用你自己的密钥或公钥做,或在本地用 openssl 与对应语言的库算。需要生成 HMAC 做对照,可以用宝塔的 HMAC 生成工具;只想知道某段字符串解出来是什么,Base64 编解码工具 更直接。
三种常见报错,各卡在哪一步
① 段数不对:只贴了 header.payload 两段,页面提示「JWT 应为 3 段(以 . 分隔),当前为 2 段」,不渲染任何字段。复制时被截断、或从日志里只抠了前两段,都会这样。
② payload 不是 JSON:把 payload 段换成一段普通文本后,报错是引擎原文 Unexpected token 'h', "this is not json" is not valid JSON(Chrome 实测,其他浏览器内核的措辞可能略有差异)。解出来不是合法 JSON 对象,说明那段 base64 内容本身被改过,或者根本不是 payload。
③ 带上了 Bearer 前缀:从 Authorization: Bearer <token> 整行复制是最常见的场景,报错是 Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.,三段全空。

处理办法一致:只留三段本身,去掉 Bearer 、换行和首尾空格。JWT 只使用 base64url 字符集,任何额外字符都会让解码直接失败。
payload 是明文,别往里放敏感数据
base64url 是编码不是加密(RFC 4648 第 5 节:用减号和下划线代替加号和斜杠,并去掉等号填充)。任何人拿到 token 都能解出 payload——本文的截图就是在没有任何密钥的情况下解出来的。
所以密码、手机号、身份证号、内部地址这类内容不要放进 payload;确实需要保密,要用 JWE 这种加密结构(五段),而不是普通的签名 JWT。
至于在线解析工具本身:这个页面写明「基于浏览器原生 atob,所有解析在本地完成,不会上传到服务器」,可以把 token 粘进去看结构。但生产环境的 token 往往等价于一次登录,交给任何页面之前,先按你所在环境的合规要求判断。
看 token 的顺序:先 alg,再 exp,最后看账户
拿到一段 token,按这三步看最快:
- alg:是不是你服务端预期的算法。看到
none就该警惕——那表示这段 token 根本没有签名。 - exp / iat:换算后的本地时间是否还在有效期内、签发时间是否合理。这一条要配自己的时钟看,工具不会替你判断。
- sub / role 等业务声明:解出来的账号和权限是不是你预期的那个。
前两步的结论来自解析后的读数加你自己的校验,第三步才轮到解析工具本身。把工具的输出当成线索,而不是结论。
常见问题
解析成功是不是说明这个 token 有效?
不是。解析只做 base64url 解码,不需要密钥,也不校验签名和有效期。实测把一段 RS256 token 的签名段替换成随便一段 base64url 字符串 Zm9vYmFy,工具照样正常显示 Header 和 Payload 表,没有任何报错;用一段 exp 比当前时间早一天的 token,表格也照常渲染,页面不提示过期。要确认 token 没被改、没过期,得在服务端用自己的密钥或公钥重算签名。
粘贴 token 后报 atob 的 not correctly encoded 是什么原因?
多半是整行复制进来的,带上了 Bearer 前缀或者换行、空格。JWT 三段只含 base64url 字符,实测输入 Bearer 加一个空格再粘 token,工具直接给出 Failed to execute atob on Window: The string to be decoded is not correctly encoded,表格全空。去掉 Bearer 前缀和首尾空白再粘即可。
提示「JWT 应为 3 段」怎么办?
说明字符串被截断了。实测只贴 header.payload 两段,页面提示「JWT 应为 3 段(以 . 分隔),当前为 2 段」,并且不渲染任何字段。按 RFC 7519 的定义,JWT 的紧凑格式固定为三段,中间用点分隔,少了签名段就不是完整的 JWT。
iat 和 exp 为什么显示成 11/6/2026 2:36:48 PM 这种格式?
payload 里的 iat、exp、nbf 是 NumericDate,即 UTC 秒数。工具把秒换算成浏览器本地时区,再按 M/D/YYYY h:mm:ss AM/PM 的格式显示在原始数字下面。截图里 iat 是 1793947008、显示为 11/6/2026 2:36:48 PM,exp 是 1793954328、显示为 11/6/2026 4:38:48 PM,两者正好差 7320 秒(2 小时 2 分)。对不上时先查浏览器时区,别把它当 ISO 8601 用。
能在 JWT 的 payload 里放密码、手机号吗?
不要放。payload 只是 base64url 编码,不是加密(RFC 4648 第 5 节用减号和下划线代替加号和斜杠,并去掉等号填充),任何人拿到 token 都能解出明文——本文截图就是在没有任何密钥的情况下解出来的。需要保密的内容要用 JWE 这类加密结构,而不是普通的签名 JWT。
怎么快速看出 token 用的是哪种算法?
看 header 里的 alg。工具会在值下面用绿色补上算法全称:HS256 是 HMAC using SHA-256,RS256 是 RSASSA-PKCS1-v1_5 using SHA-256,ES256 是 ECDSA using P-256 and SHA-256,alg 为 none 时显示 No digital signature or MAC performed;实测这种 token 把签名段直接留空也照常解析、不报错。