JWT 解析 — 三段拆开看,头、载荷和 exp 到底是什么时候
把 JSON Web Token 粘进来,它按 base64url 拆出三段,把头部和载荷排成整齐的 JSON,并将 iat、nbf、exp 换算成本地时间,直接回答这个令牌按自己的说法过期了没有。不要求填密钥,也不做任何签名校验。输入的内容不会离开这台设备。
A · 头部 Header
B · 载荷 Payload
C · 逐条声明
| 声明 | 值 | 读法 |
|---|
三段令牌与三个时间声明
JWT 到底是什么
JSON Web Token 既不加密也不压缩,更不存在服务器上:它就是两份 JSON 用 base64url 写出来,再加一段对「头部.载荷」的签名。任何拿到令牌的人一秒就能读完那两份 JSON——这正是「敏感东西不要往声明里塞」的理由。三段分别是:
- 头部 Header——用了什么签名算法(
alg)、载荷是什么类型(typ),常常还带密钥线索(kid、jku)或整串证书(x5c)。 - 载荷 Payload——各项声明:
sub(这是谁的令牌)、iss(谁签发的)、exp/nbf/iat(有效期窗口)、scope/roles,再叠加业务自己加的东西。 - 签名 Signature——用签发方的密钥对前两段算出来的一串字节。只有它拦得住「改一下
sub就冒充管理员」。
因为前两段只是编码而非加密,解码不需要密钥、不需要联网。解码就做到这一步为止。
三个时间声明怎么读
iat、nbf、exp 都是从 1970-01-01 00:00:00 UTC 起算的秒数,不是本地时间字符串,它们会按浏览器自己的时区换算出来。每个时间给三种读法:带时区偏移的本地墙上时间、同一瞬间的 UTC 写法,以及相对此刻的距离(「12 分钟前过期」「还剩 2 小时 4 分钟」)——排查那枚「按理还没到期」的令牌时,你想要的其实是后面这个。
顶部那句结论只替令牌自己的说法负责。一枚完全没过期的令牌,服务端照样可以拒绝:会话被吊销、用户被删除、aud 对不上、密钥轮换过。反过来,一枚过期但从没被检查的令牌还能继续用——有效期是签发方写下的承诺,接收方可以遵守也可以无视。
秒数不该有那么多位数:超过 1e11 秒已经排到 5138 年之后,所以 1750000000000 大概率是把 Date.now() 直接塞进了想要「秒」的字段。这样的值按毫秒读,并且会明说是猜的。
为什么签名一段不管
验签意味着重算签名,重算意味着密钥。HS256 用的是共享密钥,那种东西永远不该出现在浏览器里;RS256 / ES256 要拿公钥,而公钥得去签发方下载,那就等于把你的令牌元数据发上网络。所以不要密钥,也不拿你的令牌去问任何人,「签名有效」也从不出现——第三段只显示字节数和一句实话。
从头部倒是能读出真正有用的危险信号:alg: none 表示令牌声明自己不需要签名,接受这种令牌的实现等于允许任何人冒充任何人;alg 干脆缺失更糟,因为经典的库漏洞就是「算法跟着令牌里写的走」。
什么时候结果要再确认一遍
- 载荷是数组或标量:按原样打印,不列表格——能解析,但几乎不可能是真令牌。
- JSON 里重复的键:解析时只留最后一个,于是写了两个
exp的令牌在这里只显示一个。这是 JSON 的规则,不是这里的取舍,也是「手写令牌很危险」的原因之一。 - 超过 15 位有效数字的整数:
sub: 1234000000000000001尾数可能被改写,提示行会点名是哪几个字段。 "b64": false(RFC 7797):第二段本来就是明文 JSON;头部解析成功后,这个标记会被照着处理,并说明发生了什么。- 不干净 UTF-8 的载荷:解不出来的字节序列按 Web 惯例变成一个 并计数报告,不会悄悄删掉整段。有些签发方把二进制 id 塞进声明,那种值怎么显示都没意义。
常见问题
Q:能用来伪造令牌吗? 不能。改掉任何一条声明都会让签名对不上,而这里既不持有也不需要密钥。展示出来的只是「令牌说了什么」——这也回答了「为什么我在公开响应里能读出别人的用户 id」:能读,那是暴露它的那套系统的问题,不是 JWT 的问题。
Q:明明显示还在有效期内,服务端为什么给 401?
「在自己的窗口里」不等于「会被接受」。接收方还要验签、检查 iss 和 aud 的白名单、允许的时钟误差,通常还有一张吊销表。结论只针对载荷里写下的声明。
Q:我粘的值末尾带 =,怎么解不出来?
补位是允许解码的,也会照样解(JWT 按规范不补位,所以带 = 说明它是从别的 base64 字段复制来的)。如果还是不行,那多半根本不是 JWT,可以拿 Base64 那页试试。
Q:粘的令牌会被传走吗?
拆分、解码、排版都是在浏览器里跑的循环:没有 eval,令牌不经过任何服务器,输入的内容不会离开这台设备。