JSON Web Token (JWT) 是现代 Web 应用中最广泛使用的无状态认证标准。本指南详细解释 JWT 的工作原理、结构、安全最佳实践和常见陷阱。
什么是 JWT?
JWT 是一种紧凑的、URL 安全的令牌,用于在两方之间传递声明(数据)。它由三部分组成,用点分隔:Header.Payload.Signature。令牌经过 Base64URL 编码,适用于 HTTP 头、Cookie 和 URL。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header Payload Signature
(algorithm) (user claims) (verification hash)JWT 结构解析
Header(头部)
头部标识用于签名令牌的算法:
{
"alg": "HS256", // Algorithm: HMAC-SHA256
"typ": "JWT" // Token type
}Payload(负载/声明)
负载包含声明 — 关于用户和元数据的信息:
{
"sub": "1234567890", // Subject (user ID)
"name": "John Doe", // Custom claim
"email": "john@example.com",
"role": "admin", // Custom claim
"iss": "https://api.example.com", // Issuer
"aud": "https://app.example.com", // Audience
"iat": 1516239022, // Issued at (Unix timestamp)
"exp": 1516242622 // Expiration (1 hour later)
}Signature(签名)
签名确保令牌未被篡改:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret_key
)JWT 认证流程
JWT vs 基于 Session 的认证
| Feature | JWT (Token-based) | Session (Server-side) |
|---|---|---|
| Storage | Client-side (cookie/memory) | Server-side (database/Redis) |
| Scalability | Stateless — scales easily | Requires shared session store |
| Server load | No DB lookup per request | DB/Redis lookup each request |
| Revocation | Hard (need blacklist) | Easy (delete session) |
| Size | Larger (contains claims) | Small session ID |
| Mobile-friendly | Yes (no cookies needed) | Harder without cookies |
| Security | Vulnerable if secret leaked | Vulnerable to session fixation |
常见 JWT 声明
| Claim | Name | Description |
|---|---|---|
iss | Issuer | Who created the token (e.g., your auth server URL) |
sub | Subject | The user or entity the token represents (usually user ID) |
aud | Audience | Intended recipient (e.g., your API URL) |
exp | Expiration | Unix timestamp after which the token is invalid |
iat | Issued At | Unix timestamp when the token was created |
nbf | Not Before | Token is not valid before this timestamp |
jti | JWT ID | Unique identifier to prevent token replay |
安全最佳实践
- 始终验证签名 — 不要信任未验证的 JWT。使用经过充分测试的库。
- 使用短过期时间 — 将 exp 设置为 15-60 分钟。使用刷新令牌处理更长的会话。
- 存储在 httpOnly cookie 中 — 防止 XSS 攻击读取令牌。添加 Secure 和 SameSite 标志。
- 不要在负载中存储敏感数据 — JWT 负载是 Base64 编码的,不是加密的。任何人都可以解码。
- 使用强密钥 — HMAC 至少使用 256 位随机性。RSA/EC 使用适当的密钥长度。
- 实现令牌撤销 — 使用黑名单或短期令牌配合刷新令牌。
常见 JWT 漏洞
攻击者修改头部以不使用算法。修复:始终在服务端验证算法,不接受 "none"。
弱 HMAC 密钥可被暴力破解。修复:至少使用 256 位随机密钥。
被盗的令牌可在过期前被重用。修复:使用短过期时间,绑定 IP/设备,实现刷新令牌轮换。
实现示例
// Node.js with jsonwebtoken library
const jwt = require('jsonwebtoken');
// Sign (create token)
const token = jwt.sign(
{ sub: user.id, name: user.name, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '1h', issuer: 'https://api.example.com' }
);
// Verify (validate token)
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
issuer: 'https://api.example.com',
algorithms: ['HS256'], // Prevent alg switching!
});
console.log(decoded.sub); // User ID
} catch (err) {
// Token is invalid or expired
console.error('JWT verification failed:', err.message);
}
// Express middleware
function authMiddleware(req, res, next) {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
const token = authHeader.split(' ')[1];
try {
req.user = jwt.verify(token, process.env.JWT_SECRET);
next();
} catch {
return res.status(401).json({ error: 'Invalid token' });
}
}即时解码和检查任何 JWT
FAQ
JWT 是加密的吗?
不是。标准 JWT (JWS) 只是签名,未加密。负载是 Base64URL 编码的,任何人都可以解码。如需隐藏负载内容,使用 JWE。
应该将 JWT 存储在 localStorage 还是 cookie 中?
httpOnly cookie 更安全,因为 JavaScript 无法访问,防止 XSS 窃取令牌。localStorage 容易受 XSS 攻击但更容易实现。
如何处理 JWT 过期?
使用短期访问令牌(15-60分钟)配合长期刷新令牌。访问令牌过期时,用刷新令牌获取新的。
HS256 和 RS256 有什么区别?
HS256 使用共享密钥签名和验证。RS256 使用私钥签名、公钥验证 — 适合分布式系统。
可以撤销 JWT 吗?
JWT 是无状态的,不能直接撤销。常见方法:维护令牌黑名单、使用极短过期时间、或在数据库中实现令牌版本系统。