JWT 原理:Header、Payload 与 Signature

JWT(JSON Web Token)是一种紧凑的 Token 表示格式。常见的“签名 JWT”由三段组成,中间使用英文句点连接:

xxxxx.yyyyy.zzzzz
Header.Payload.Signature

1. Header

Header 描述 Token 使用的类型和签名算法:

{
  "alg": "HS256",
  "typ": "JWT"
}

Header 会被 JSON 序列化并进行 Base64URL 编码。它不是秘密。

2. Payload

Payload 保存 Claims(声明),也就是 Token 要表达的信息:

{
  "sub": "user-123",
  "role": "user",
  "iss": "nloop-api",
  "aud": "nloop-client",
  "iat": 1786492800,
  "exp": 1786493700
}

常见注册声明:

声明 含义 示例用途
sub Subject,主体 用户 ID
iss Issuer,签发方 nloop-api
aud Audience,接收方 nloop-client
iat Issued At,签发时间 计算 Token 年龄
exp Expiration Time,过期时间 超时后拒绝
nbf Not Before 指定何时开始生效
jti JWT ID Token 唯一标识

iatexpnbf 使用 NumericDate:从 Unix Epoch 开始计算的秒数,不是 JavaScript 常用的毫秒数。

Payload 可以放少量、非敏感且验证后确实需要的身份信息,例如用户 ID、Token 类型。不要放:

3. Signature

以 HS256 为例,签名概念可简化为:

encodedHeader  = base64Url(header)
encodedPayload = base64Url(payload)
signingInput   = encodedHeader + "." + encodedPayload
signature      = HMAC-SHA256(signingInput, secret)

最终 Token 是:

encodedHeader.encodedPayload.encodedSignature

如果攻击者把 role: "user" 修改成 role: "admin",但没有 Secret,就无法生成匹配的新签名。服务端调用 jwt.verify() 时会发现签名不一致。

4. 签名保护什么

签名可以帮助验证:

签名不能保证:

因此 Bearer Token 必须通过 HTTPS 传输,并设置合理的过期时间。

5. decode() 为什么不可信

解码只做格式转换:

Base64URL 字符串 -> JSON

任何人都能构造三段字符串。jwt.decode() 不会证明签名正确,也不会把数据变成可信数据。

const payload = jwt.decode(token); // 只能查看
const payload = jwt.verify(token, secret); // 验证后才可能用于认证

即使通过 verify(),返回的数据仍来自外部请求。代码还应检查它是不是对象,以及 subtokenType 等业务字段是否存在且类型正确。

6. HS256 与 RS256

HS256

RS256

当前教程选择 HS256,是为了把注意力放在认证流程。生产系统应根据部署边界和密钥管理能力选择算法。

7. JWT、JWS 和 JWE

jsonwebtoken 的主要用途是签发和验证 JWT/JWS,不应把它理解为“JWT 内容加密库”。

8. 无状态的含义

服务端验证 Access Token 时,可以只依赖签名密钥和 Token 本身,不一定查询会话表,这常被称为无状态认证。

但无状态也带来限制:已签发且尚未过期的 JWT 很难立即撤销。常见应对策略包括:

因此,“使用 JWT”不等于“认证系统完全不需要数据库状态”。