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)——标准规定的字段:
| 字段 | 全称 | 说明 |
|---|---|---|
iss | Issuer | 签发者 |
sub | Subject | 主题(通常是用户 ID) |
aud | Audience | 接收方 |
exp | Expiration | 过期时间(Unix 时间戳) |
iat | Issued At | 签发时间 |
nbf | Not Before | 生效时间 |
jti | JWT 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 认证流程
- 用户使用用户名密码登录
- 服务端验证成功后,生成 JWT 并返回给客户端
- 客户端将 JWT 存储在
localStorage或 Cookie 中 - 后续请求在
Authorization头中携带 JWT - 服务端验证签名有效性和过期时间
// 客户端发送请求
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 不存储敏感信息
• 始终验证签名和过期时间
• 明确指定允许的算法
• 使用强密钥(HS256 至少 256 位)
• 设置合理的过期时间
• 使用 HTTPS 传输
• Payload 不存储敏感信息
🛠️ 在线 JWT 解码与验证工具
打开 JWT 工具 →延伸阅读
- Base64 编解码详解 — JWT 使用 Base64Url 编码
- 哈希函数详解 — JWT 签名中的 HMAC-SHA256
- JSON 格式化指南 — JWT Payload 本质上是 JSON
- 时间戳转换指南 — JWT 的 exp/iat 使用 Unix 时间戳