正在加载,请稍候…

什么是 JWT?JSON Web Token 工作原理(附示例)

从入门到进阶的 JWT 完整指南:什么是 JWT、三部分结构、与 Session 的对比、认证流程及常见安全陷阱。

什么是 JWT?JSON Web Token 工作原理(附示例)

什么是 JWT?

JSON Web Token(JWT) 是一种紧凑、URL 安全的字符串,用于在双方之间传递经过验证的声明。可以把它想象成一张签名的数字票据:服务器签发它,客户端存储它,每次后续请求都将其作为身份证明提交。

JWT 是自包含的——令牌本身携带所需数据,因此服务器无需在每次请求时都查询数据库。

什么是 JWT?JSON Web Token 工作原理(附示例)示意图

JWT 的三部分

一个 JWT 看起来像这样:

xxxxx.yyyyy.zzzzz

用点号分隔后,得到三个 Base64URL 编码的部分:

1. 头部(Header)

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

声明令牌类型和签名算法。常见算法:HS256(HMAC-SHA256,对称)、RS256(RSA,非对称)、ES256(ECDSA)。

2. 载荷(Payload / Claims)

{
  "sub": "user_123",
  "name": "Alice",
  "role": "admin",
  "iat": 1716825600,
  "exp": 1716829200
}

包含实际数据(“声明”)。标准注册声明:

声明 含义
sub 主题(用户 ID)
iss 签发者
aud 受众
exp 过期时间(Unix 时间戳)
iat 签发时间
nbf 生效时间

重要:载荷仅经过 Base64URL 编码,并未加密。任何人都可以解码。切勿在载荷中放置密码或敏感的个人身份信息。

3. 签名(Signature)

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

签名用于验证令牌未被篡改。如果头部或载荷的任何一位发生变化,签名将失效。

什么是 JWT?JSON Web Token 工作原理(附示例)示意图

JWT 认证工作原理(分步说明)

1. 用户发送凭据(用户名 + 密码)到 POST /login
2. 服务器验证凭据
3. 服务器创建包含用户声明的 JWT,并使用密钥签名
4. 服务器将 JWT 返回给客户端
5. 客户端存储 JWT(内存、localStorage、httpOnly cookie)
6. 客户端在每次请求中发送 JWT:Authorization: Bearer <token>
7. 服务器在每次请求时验证签名——无需数据库查询
8. 服务器直接从载荷中读取声明

JWT 与 Session 令牌对比

因素 JWT Session
存储位置 客户端 服务器端(数据库/Redis)
可扩展性 无状态,易于扩展 需要粘性会话或共享存储
撤销 困难(必须等待过期) 容易(从存储中删除)
载荷 携带数据 仅一个不透明 ID
大小 较大(约 500 字节以上) 较小(16–32 字节)
最佳适用场景 API、微服务 单体 Web 应用

经验法则:对于需要即时注销的传统 Web 应用,使用 Session;对于 API 和分布式系统,使用 JWT。

常见 JWT 安全错误

1. 使用 "alg: none"

一些早期库在 alg 设置为 "none" 时会接受未签名的令牌。始终显式验证算法:

jwt.verify(token, secret, { algorithms: ['HS256'] });

什么是 JWT?JSON Web Token 工作原理(附示例)示意图

2. 将 JWT 存储在 localStorage 中

localStorage 可通过 JavaScript 访问,容易受到 XSS 攻击。对于基于浏览器的应用,优先使用 httpOnly cookie

3. 服务之间共享对称密钥

使用 HS256 时,每个服务都需要相同的密钥来验证令牌——这意味着任何服务也可以签发令牌。使用 RS256/ES256,这样只有认证服务拥有私钥;其他服务只需要公钥。

4. 过期时间过长且无刷新机制

一个 30 天有效且无法撤销的 JWT 是安全负担。推荐模式:

  • 访问令牌:短生命周期(15 分钟)
  • 刷新令牌:长生命周期(7–30 天),存储在 httpOnly cookie 中,每次使用后轮换

5. 未验证 exp

始终验证过期时间。大多数库会自动执行此操作,但请确认它未被禁用。

无需库解码 JWT

function decodeJwt(token) {
  const [headerB64, payloadB64] = token.split('.');
  const decode = (str) => JSON.parse(atob(str.replace(/-/g, '+').replace(/_/g, '/')));
  return {
    header: decode(headerB64),
    payload: decode(payloadB64),
  };
}

const { payload } = decodeJwt(myToken);
console.log(payload.sub); // user_123

常见问题

问:可以在没有 HTTPS 的情况下使用 JWT 吗? 不可以。始终使用 HTTPS。没有 TLS,令牌可能被拦截并重用(重放攻击)。

问:如何在 JWT 过期前使其失效? 选项:维护服务器端黑名单/拒绝列表、使用短过期时间 + 刷新令牌、或更改签名密钥(会使所有令牌失效)。

问:JWT 载荷的最大大小是多少? 没有硬性限制,但请保持较小(1 KB 以下)。较大的令牌会增加请求开销。避免存储每次请求都需要从数据库查询的内容。

问:JWT 和 OAuth 是一样的吗? 不是。JWT 是一种令牌格式。OAuth 2.0 是一种授权协议,可以使用 JWT 作为其令牌格式。

→ 使用 JWT 解析器 检查并解码任何 JWT 令牌。