JWT 完全指南:结构、签名与安全使用

JSON Web Token(JWT)是一种开放标准(RFC 7519),用于在各方之间安全地传输声明信息。它在现代 Web 应用的身份认证和授权中被广泛采用。本文将全面解析 JWT 的内部结构、签名机制和安全使用方法。

JWT 的三段式结构

一个 JWT 由三个部分组成,用点号 . 分隔:

Header.Payload.Signature

示例 JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IuW8oOS4iSIsImlhdCI6MTcwOTI4MDAwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

1. Header(头部)

Header 是一个 JSON 对象,经过 Base64Url 编码。它声明了令牌类型和签名算法:

{
  "alg": "HS256",   // 签名算法
  "typ": "JWT"      // 令牌类型
}

2. Payload(载荷)

Payload 包含声明(Claims),也是一个 Base64Url 编码的 JSON 对象。声明分为三类:

注册声明(Registered Claims)——标准规定的字段:

字段全称说明
issIssuer签发者
subSubject主题(通常是用户 ID)
audAudience接收方
expExpiration过期时间(Unix 时间戳)
iatIssued At签发时间
nbfNot Before生效时间
jtiJWT ID令牌唯一标识
{
  "sub": "1234567890",
  "name": "张三",
  "role": "admin",
  "iat": 1709280000,
  "exp": 1709366400
}
⚠️ 注意:Payload 只是 Base64Url 编码,不是加密。任何人都可以解码并查看其中的内容。不要在 Payload 中存放密码、密钥等敏感信息。

3. Signature(签名)

签名用于验证令牌未被篡改。计算方式取决于所选算法:

// HMAC-SHA256 签名
HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

// RSA-SHA256 签名
RSASHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  privateKey
)

签名算法对比

算法类型密钥适用场景
HS256对称共享密钥单体应用、信任双方
HS384/HS512对称共享密钥需要更高安全强度
RS256非对称公钥/私钥微服务、第三方验证
ES256非对称椭圆曲线密钥高性能、移动端
💡 选择建议:如果签发和验证在同一个服务上,使用 HS256 最简单。如果需要第三方验证(如微服务架构),使用 RS256,这样只需分发公钥。

JWT 认证流程

  1. 用户使用用户名密码登录
  2. 服务端验证成功后,生成 JWT 并返回给客户端
  3. 客户端将 JWT 存储在 localStorage 或 Cookie 中
  4. 后续请求在 Authorization 头中携带 JWT
  5. 服务端验证签名有效性和过期时间
// 客户端发送请求
fetch('/api/user/profile', {
  headers: {
    'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIs...'
  }
})

// 服务端验证(Node.js)
const jwt = require('jsonwebtoken');

try {
  const decoded = jwt.verify(token, SECRET_KEY);
  console.log(decoded.sub); // "1234567890"
} catch (err) {
  console.log('令牌无效:', err.message);
}

JWT 安全陷阱

1. alg: "none" 攻击

攻击者将 Header 中的 alg 改为 "none",跳过签名验证。

🚫 防御:服务端必须明确指定接受的算法,拒绝 "none"
// ❌ 不安全:接受任何算法
jwt.verify(token, secret);

// ✅ 安全:明确指定算法
jwt.verify(token, secret, { algorithms: ['HS256'] });

2. 密钥混淆攻击

如果服务端同时支持 HS256 和 RS256,攻击者可能用 RS256 的公钥作为 HS256 的密钥来伪造签名。

3. 令牌过期与撤销

JWT 一旦签发,在过期前无法主动撤销。应对方案:

  • 设置较短的过期时间(如 15 分钟)
  • 使用 Refresh Token 机制续期
  • 维护黑名单(牺牲无状态性)

4. 存储安全

// ❌ localStorage 容易被 XSS 攻击读取
localStorage.setItem('token', jwt);

// ✅ 使用 HttpOnly + Secure + SameSite Cookie
Set-Cookie: token=eyJ...; HttpOnly; Secure; SameSite=Strict; Path=/
💡 安全清单:
• 始终验证签名和过期时间
• 明确指定允许的算法
• 使用强密钥(HS256 至少 256 位)
• 设置合理的过期时间
• 使用 HTTPS 传输
• Payload 不存储敏感信息

🛠️ 在线 JWT 解码与验证工具

打开 JWT 工具 →

延伸阅读