JWT(JSON Web Token)是用点号连接的三段 Base64url 编码内容。解码一份 JWT 来读取 claims 只需要一次函数调用,且不需要任何密钥 —— 这恰恰说明了 JWT payload 不是放敏感数据的安全地方。本指南讲解 JWT 是什么、如何解码每一部分,以及解码与验证之间至关重要的区别。
什么是 JWT?
JWT 是一种紧凑、URL 安全的 token,用来在各方之间携带 claims —— 通常是用户身份与权限。它由 RFC 7519 标准化。一个 token 看起来像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0IiwibmFtZSI6IkFkYSJ9.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk两个点号把它切分为三部分:header、payload 和 signature。
三个组成部分
- Header —— 描述签名算法的一个小 JSON 对象,例如
{"alg":"HS256","typ":"JWT"}。 - Payload —— 一组 claims 的 JSON 对象,例如
sub(subject)、exp(过期时间),以及任何自定义字段。 - Signature —— 对 header 与 payload 的密码学签名,用于检测 篡改。它不是人类可读的,也不是 JSON。
header 与 payload 都是 Base64url 编码的 JSON。签名由 JWS(RFC 7515)产生。
如何在 JavaScript 中解码 JWT
按点号切分,然后对前两段做 Base64url 解码。注意 JWT 用的是Base64url(把 + 和 / 换成 - 与 _,并且不填充),因此解码前需要做一次换字符:
function decodeJwt(token) {
const [headerB64, payloadB64] = token.split('.');
const decode = (b64url) => {
// Base64url → Base64,再解码并 parse
const b64 = b64url.replace(/-/g, '+').replace(/_/g, '/');
const json = atob(b64);
return JSON.parse(json);
};
return { header: decode(headerB64), payload: decode(payloadB64) };
}
decodeJwt(token);
// {
// header: { alg: "HS256", typ: "JWT" },
// payload: { sub: "1234", name: "Ada" }
// } 为了完整的 Unicode 安全,请把 Base64 解码为字节,再通过 TextDecoder 处理,而不是只用 atob。
如何在 Python 中解码 JWT
import base64, json
def decode_jwt(token):
header_b64, payload_b64, _sig = token.split('.')
def decode(part):
# 补回 Base64url 剥掉的填充
padded = part + '=' * (-len(part) % 4)
return json.loads(base64.urlsafe_b64decode(padded))
return decode(header_b64), decode(payload_b64)解码不是验证
这是关于 JWT 最重要的一点。任何人都能解码一个 token 并读它的 payload —— 不需要任何密钥。解码告诉你 token 声称了什么;但它并不告诉你这些 claims 是否可信。
要信任一个 token,你必须用经过 审计的库验证签名(对照服务器的密钥或公钥)—— 不要自己造轮子:
import jwt from 'jsonwebtoken';
// 验证签名 AND 检查过期;不通过就抛错
const claims = jwt.verify(token, process.env.JWT_SECRET);
// jwt.decode(token) —— 不验证;仅用于查看服务端的验证才让 JWT 成为凭证。把解码后的 payload 当作可信、却不去检查签名 —— 这是经典的认证漏洞。
JWT payload 没有被加密
因为 payload 只是 Base64url 编码的 JSON,它提供了零 的机密性。这与 Base64 不是加密 谈到的是同一个误解:编码任何人都能逆转。
永远不要把密码、完整信用卡号或敏感个人信息放进 JWT payload。如果你确实需要一个加密的 token,请用 JWE(JSON Web Encryption,RFC 7516)—— 这是另一个、相对少见的标准。也避免把真实 token 贴进不可信的在线解码器;见 为什么不要把敏感 JSON 贴到在线工具里。
校验顺序:先验签,再看 claims
生产环境里验证一个 token 时,顺序很重要。一个安全的检查清单:
- 算法白名单 —— 不在你期望集合里的一律拒绝(例如
['RS256'])。永远不要接受none。 - 签名 —— 用正确的密钥验证(按
kid在 JWKS 里查)。 exp—— token 不能已过期。nbf(not-before)—— token 不能太早被使用。iat—— 合理性检查:签发时间不能荒谬地在未来(时钟漂移除外)。iss—— 签发者必须是你预期的可信权威。aud—— audience 必须包含你的服务。
多数 JWT 库默认会做 1-4 步,并要求你显式开启 6-7。在 iss 和 aud 被检查之前,一个本来用于你生态里另一个服务的合法 token,可能被重放到你的服务上。
常见标准 claims
| Claim | 含义 |
|---|---|
iss | 签发者 —— 创建 token 的人 |
sub | 主体 —— token 描述的是谁(通常是用户 ID) |
aud | 受众 —— token 的目标接收方 |
exp | 过期时间 —— 此 Unix 时间戳之后 token 无效 |
iat | 签发时间 —— token 创建时的 Unix 时间戳 |
nbf | 不早于 —— 此时间之前 token 无效 |
kid header 与 alg: none 陷阱
在真实 JWT 中出现得太频繁的两个 header 字段,值得让你一眼就认出来:
kid(key ID) —— 一个提示,告诉验证方:在一个密钥集(JWKS)里哪一把 签了这个 token。它放在 header 里,这样验证方在验签之前就能选对密钥。把kid当作不可信输入:到一个固定白名单(或拉取到的 JWKS)里查它,永远不要把它当文件路径、SQL 参数或 URL 用。alg: "none"—— 一个合法但历史上极危险的算法,意思是「无签名」。解码器照样能解码,宽松的验证方甚至会接受 —— 任何伪造的 payload 都会变成「合法」 token。明确拒绝alg: none,并把你的库配置为只接受你期望的精确算法(例如['RS256']),而不是相信 token 自己 header 里声称的算法。
在浏览器里解码 JWT
想要快速查看 token 的 claims,把任意 Base64url 段贴进 fixjson 的 Base64 解码器 —— 它在本地解码到底层 JSON,不向任何服务器发送数据。对非敏感 token 你也可以用专门的调试器,但对包含真实用户数据的 token,本地优先的工具才是安全选择。
常见问题
怎么解码 JWT?
按点号切分 token,对前两部分(header 与 payload)做 Base64url 解码得到 JSON。第三部分是签名,不是人类可读。解码不需要密钥。
解码 JWT 和验证它一样吗?
不一样。解码只是读 claims,谁都能做。验证是用密钥或公钥校验签名,确认 token 真实且未被篡改。信任 token 之前请始终在服务端验证。
JWT 里的数据谁都能读吗?
能。payload 是 Base64url 编码的,不是加密 —— 对任何拿到 token 的人来说就是明文。永远不要在 JWT payload 里存放秘密。见 Base64 不是加密。
JWT、JWS 和 JWE 有什么区别?
一个签名的 JWT 使用 JWS(RFC 7515)—— payload 可读、能检测篡改。JWE(RFC 7516)会真正加密 payload,提供机密性。你日常见到的普通 JWT 就是 JWS。
kid claim 是用来干嘛的?
kid 是一个 header 字段(不是 payload claim),指明 JWKS 里哪一把密钥签了 token,让验证方能挑对那把。在你掌控的固定密钥集里查它 —— 永远不要把它当文件路径或 URL 信任。
为什么 alg: "none" 危险?
意思是「无签名」。如果验证方尊重 token 自己声明的算法,它就会接受任何伪造的 payload。请始终把库配置成只接受你期望的精确算法(例如 ['RS256']),并明确拒绝 none。
本地检视 token
- Base64 编码与解码 —— 在浏览器里解码 JWT 的 header/payload 段
- Base64 不是加密 —— 为什么 JWT payload 对任何人可读
- 为什么不要把敏感 JSON 贴到在线工具里 —— 安全地处理真实 token
- RFC 7519:JSON Web Token —— 标准本身