jwt在线解析:三段结构、exp 过期判断与常见报错

接口返回一个几百字符的 eyJhbGciOi... 字符串,你想知道里面装了什么、什么时候过期——jwt在线解析做的就是这件事:把三段 Base64URL 还原成 JSON,直接告诉你算法、签发时间、过期时间和当前状态。

但两条底线必须先记住:payload 只是编码,不是加密,任何人都能解出来;签名没有密钥就验不了。在线工具告诉你的只是「它写了什么」,不是「它对不对」。

一、三段结构:header.payload.signature

一个 JWT 就是三个用英文句点隔开的字符串,每一段各自独立做 Base64URL 编码:

段 里面是什么 能不能直接读懂
Header 算法 alg、类型 typ 等头部参数 能,就是一段 JSON
Payload 各种声明(claims) 能,就是一段 JSON
Signature 对前两段做的签名 不能,它是一串摘要字节

最容易和 Base64 混淆的一点:JWT 用的是 Base64URL,不是标准 Base64。 标准 Base64 的字母表第 62、63 位是 + 和 /,末尾还会补 = 凑长度;而 JWT 要放进 URL 和 HTTP 头,+ 在查询串里会被解析成空格,/ 是路径分隔符,所以 RFC 4648 单独定义了 URL 安全变体:+ 换成 -,/ 换成 _,去掉所有 = 补位。

后果就是你把 JWT 某一段直接粘进普通 Base64 解码框,有时能出结果、有时报「非法字符」或「长度错误」。另外,整串 JWT 不是一次 Base64,三段各自编码后用句点拼接,把整串拿去解码永远得不出东西。

二、注册声明速查表

声明 含义 取值与注意
iss 签发者 确认 token 是谁发的
sub 主体,通常是用户 ID 只放标识,别放手机号、邮箱
aud 受众,即目标服务 字符串或数组,对不上应拒绝
exp 过期时间 秒级 Unix 时间戳,不是毫秒
nbf 最早生效时间 秒级,当前时间早于它应拒绝
iat 签发时间 秒级,可用来算 token 年龄
jti 本 token 唯一 ID 防重放、做吊销黑名单的钥匙

exp/nbf/iat 在规范里叫 NumericDate,单位统一是秒。JS 里 Date.now() 给的是毫秒,很多线上事故就出在少写了一个 / 1000:签发端把毫秒填进去,exp 会变成公元五万年,所有过期判断失效;反过来校验端拿毫秒比秒,每个 token 都被判为已过期。

三、payload 没有加密,别往里放敏感信息

解码过程和「解」一个 Base64 字符串没有任何区别。任何拿到 token 的人——前端、网关、日志系统、CDN——都能在几毫秒内读出每一个字段。因此这些绝不能放进 payload:密码与密钥、手机号与身份证号、银行卡号、能定位到个人的详细信息。

真正加密的 JWT 叫 JWE(RFC 7516),平时看到的 eyJ... 几乎都是只签名不加密的 JWS:header 的 alg 是 HS256/RS256 即为 JWS,出现 A128GCM、RSA-OAEP 才是 JWE。

想看看里面写了什么,用工具最快:

👉 JWT 在线解析(不上传服务器)

本站这个工具会给出算法、类型、签发时间、过期时间(按本地时区格式化)、状态标签(有效/已过期/尚未生效/不过期),以及三段分色展示和 payload 逐条声明列表。它不校验签名——验签需要密钥,本工具只做本地解码。所以生产环境的 token 别往任何在线工具里粘。

四、没有密钥就验不了签名,alg=none 是历史漏洞

签名的计算对象是 base64url(header) + "." + base64url(payload) 这一段:HS256 用同一密钥做 HMAC-SHA256,RS256 用私钥签名、公钥验签。所以没有密钥的在线工具永远只能解、不能验,这不是偷懒,是数学上做不到。

早期若干 JWT 库允许 header 里 alg 为 none 并据此跳过签名校验,攻击者把 payload 改成 {"sub":"admin"}、把 alg 改成 none、删掉签名段,服务端照样放行。修复方式只有一个:服务端强制算法白名单,不按 token 里写的来。同理,RS256 改成 HS256 拿公开公钥当 HMAC 密钥的混淆攻击,结论也是同一条:算法必须由服务端配置决定,绝不能信任 header 里的 alg。

五、常见报错对照表

报错 / 现象 根因 怎么修
TokenExpiredError: jwt expired now > exp 用 refresh token 换新;检查服务器时钟
JsonWebTokenError: invalid signature 密钥不一致 / 算法不匹配 确认签发与校验同一密钥同一算法
JsonWebTokenError: jwt malformed 不是三段 检查有没有带 Bearer 前缀、多复制或少复制
NotBeforeError: jwt not active now < nbf 签发端时钟偏快,或 nbf 填成了毫秒
invalid character / 解出乱码 用标准 Base64 去解 Base64URL 先 -→+、_→/,再补 = 到 4 的倍数
中文全是问号或方块 签发端没用 UTF-8 编码 payload 统一 UTF-8 后重签

最常见的两个诱因:从 Authorization 头复制时带了 Bearer 前缀,以及被邮件客户端或 IDE 自动折行插进了换行。

六、排错清单(按顺序过一遍)

  1. 去掉 Bearer 前缀和首尾空白,确认是单行且恰好两个句点
  2. 确认字符集 URL 安全:只有 A-Z a-z 0-9 - _ .,出现 + / 说明被标准 Base64 处理过
  3. 先看 header 的 alg,确认它和服务端配置的算法一致
  4. 看 exp 换算出来的日期,判断是不是毫秒单位填错
  5. 校验失败先确认密钥来源,再看算法
  6. 状态显示「不过期」的,回签发端补 exp
  7. 生产 token 一律不要粘到在线工具,用本地命令行解
# Node 一行解出 payload(Node 15+ 支持 base64url)
node -e 'const p=process.argv[1].split(".")[1];console.log(JSON.stringify(JSON.parse(Buffer.from(p,"base64url").toString()),null,2))' "$TOKEN"
# Python:补 '=' 是关键
import base64, json
def jwt_payload(token):
    seg = token.split('.')[1]
    return json.loads(base64.urlsafe_b64decode(seg + '=' * (-len(seg) % 4)).decode('utf-8'))

七、一句话速查

结构      base64url(header).base64url(payload).base64url(signature)
编码      + → -   / → _   去掉 = 补位(RFC 4648 §5)
exp/iat/nbf  单位 = 秒(NumericDate),不是毫秒
过期判断  now > exp 拒绝;now < nbf 拒绝;留 5~60 秒 leeway
安全      算法由服务端白名单决定,不信 header 的 alg;alg=none 一律拒绝
payload   只是编码不是加密,禁止放密码 / 手机号 / 身份证
验签      没有密钥就验不了,在线工具只能解不能验

本文是精简版,完整版(含刷新 token 与登出失效的四种方案、Go/Java 解法、更多排错案例)见:jwt在线解析:三段结构、exp 过期判断与常见报错

相关工具:JWT 解析|Base64 编码解码|时间戳转换

posted @ 2026-09-21 01:43  k8sdoc  阅读(23)  评论(0)    收藏  举报