JWT 排查通常分为两类:令牌无法解码,以及解码成功但时间状态与预期不符。前者常见于段数不对或 Base64URL 字符被复制损坏,后者要重点核对 exp/nbf/iat 三个时间声明的比较方向。本文把每一类报错对到具体的损坏方式,所有结论均在本地解码器上实测复核(判定时间固定为 2026-01-01T00:00:00Z,Unix 秒 1767225600);文中引用的报错文案均为工具页面中文界面实际显示的文案。
先给结论
1. 报「段数不对」:先数有几个点
三段式是本工具处理 JWS 紧凑序列化 JWT 的硬性结构。把载荷误当成完整令牌粘贴(只有两段),或者把换行成两行的令牌拼在一起时多留了一个分隔符,都会得到同一条报错:
Compact token 必须包含三个以点分隔的段。
修复方向:先确认输入确实是本工具支持的三段式 JWT,再在文本编辑器里数一下 . 的个数——正确输入恰好有两个点。四段同样报这条错,常见原因是把换行后的两行拼在一起时多留了一个分隔符。
2. 报「无效 Base64URL」:三种损伤,一条文案
Base64URL 与普通 Base64 的区别在 RFC 4648 §5:字母表里的 + 换成 -、/ 换成 _,且 JWT 按规定不写 = 补位。实测有三种典型损伤,页面上统一显示同一条报错:
段不是有效的 Base64URL(JWT 不允许 padding 或标准 Base64 字符)。
页面报错只说明三段中至少有一段不是有效的 Base64URL,不会区分具体是哪一段、哪种损伤,需要逐段检查字符、长度和末尾位置:
末尾多了等号
某些语言库默认输出带补位的编码,拼进令牌后再粘贴就会带上 =。标准 JWT 的段长除以 4 只能余 0、2 或 3,永远轮不到补位符出场——看到 = 就可以确定问题在它。修复方向:优先从签发端重新获取不带补位的原始 JWT;仅在确认是非标准补位问题时,复制一份去掉末尾 = 做诊断,不要把修改后的令牌当成可信令牌使用。
字母表以外的字符
从 Word 或聊天窗口复制时,- 可能被替换成 –(en dash,比连字符略长的短横线),_ 可能被替换成全角字符;把 Authorization 头整段 Bearer eyJ… 复制进来时,前缀里的空格同样不在字母表内。修复方向:换用纯文本编辑器中转一次,或只复制 Bearer 后面的令牌本体。
末尾字符被改动:冗余位对不齐
这是最有迷惑性的一种。把真实令牌的载荷段 eyJzdWIiOiJ1c2VyLTQyIiwiZXhwIjoxNzY3MjI5MjAwfQ 的最后一个字符 Q 改成 S(复制粘贴时被替换、或手工补字写错),同样报「段不是有效的 Base64URL」。
原因:Base64URL 把每 3 个字节编成 4 个字符,末尾不足 4 个字符时,最后一个字符的低几位是必须为零的冗余位。这个载荷段的长度除以 4 余 2,因此末尾字符只有高两位携带数据,低四位必须为 0。Q 对应二进制 010000,符合要求;S 对应 010010,低四位非零,所以严格解码器会拒绝。看到这条报错,别去查签名密钥,先把三段分别与源值逐字符比对,重点检查各段末尾。
还有一种隐性的损伤:段长除以 4 余 1 时,无论字符是什么都对不齐字节边界,必然无效。截断可能产生这种长度,但仅凭余数不能确定具体丢了几个字符。
3. 三段都在,还是解不开:JSON 层的问题
「段不是有效的 JSON」
载荷解出来不是 JSON(比如把一个普通 Base64 字符串误当 JWT 粘贴),页面报:
段不是有效的 JSON。
注意区分另一种情况:解出来是合法 JSON 但不是对象(比如数组或字符串)。页面不会把载荷本身判为格式错误,载荷原文照常给出,也不会执行 NumericDate 声明检查——JWS 允许非对象载荷,因此工具仍可把它作为 JWS 载荷展示,但不会把它当作 JWT 声明对象检查。
「Header 必须包含非空字符串 alg」
头部解开了但没有 alg,或 alg 不是非空字符串,页面报:
Header 必须包含非空字符串 alg。
typ 或 kid 存在但不是字符串时,页面统一报「Header 参数类型无效」,问题来源都是头部段。
4. 时间声明的三个判定方向
设当前时刻为 now,三个声明各自只有一种「不通过」的方向,实测结果:
| exp | 过期时间 | now >= exp | 已过期(exp) |
| nbf | 生效时间 | now < nbf | 尚未生效(nbf) |
| iat | 签发时间 | iat > now | iat 晚于当前时间 |
三个最容易踩的细节:
- exp 等于当前时刻就是过期。实测 exp: 1767225600 在 now = 1767225600 时判定为「已过期」,不是「临界有效」。
- iat 在未来时,本工具只给出状态提示「iat 晚于当前时间」,不据此判定解码失败。实际服务端是否拒绝令牌,还要检查 exp、nbf、签名、发行方、受众及业务策略。多个声明同时异常时,页面状态按「已过期 > 尚未生效 > 签发在未来」的优先级展示。
- 时间声明必须是 JSON 数字。"exp": "soon" 这类字符串值会报「NumericDate 声明必须是有限 JSON 数字」;数值大到超出安全范围(实测 99999999999999)会报「发现无法安全表示的超大数字,已拒绝以避免精度丢失」——溢出的 exp 既不该当成永不过期,也不能静默显示一个错误日期。
判定用的「当前时刻」可以由调用方指定:同一令牌把当前时刻换掉,状态随之改变,所以「昨天还好的令牌今天过期」这类问题,把两边的时间戳代进去各验一次就能定位。
5. alg=none:什么时候是提示,什么时候是标记
- 头部 alg: "none" 且签名为空:状态栏显示「alg=none 表示未签名;该 token 不具备签名完整性」——这在 JWS 里是合法的编码形态,但除非系统明确使用了其他完整性保护机制,否则不能把这类令牌视为可信。
- 头部 alg: "none" 但签名段非空:提示「alg=none 通常应使用空签名段」。
- 反过来,alg 是正常算法但签名段为空:提示「签名段为空;没有可供检查的签名字节」。
也就是说,这三种情况解码本身都能成功,判读要看状态栏,不要只看「解码成功」。
6. 常见报错速查表
| 报「必须包含三个以点分隔的段」 | 段数不是 3(少粘贴一段、或多拼了一个点) | 数 . 的个数,重取完整令牌 |
| 报「段不是有效的 Base64URL」 | 末尾多 =、字符被富文本替换,或末尾损坏造成非法长度/冗余位 | 重取原始令牌;用纯文本编辑器中转;与源头逐字符比对 |
| 报「段不是有效的 JSON」 | 粘贴的不是 JWT,或载荷被截断 | 确认三段结构后重取 |
| 报「NumericDate 声明必须是有限 JSON 数字」 | 时间声明写成了字符串 | 改成 Unix 秒的数字字面量 |
| 报「发现无法安全表示的超大数字」 | 时间声明数值溢出 | 核对时间戳单位后重新签发 |
| 整体状态「已过期(exp)」 | now >= exp,边界相等也算过期 | 重新签发或延长有效期 |
7. 在线复核解码结果
本地排查完,把令牌贴回解码器复核一遍整体状态最省事:头部、载荷、签名三段的解码结果与三类时间声明的判定会一次性给出。可以在 JWT 解码检查器 中粘贴令牌复核——它完全在浏览器本地解码,不上传令牌,也没有密钥输入,因此只适合「解不开、状态不对」这类排查,不承担签名验证。



