4. JWT 生成与签发流程:服务端生成 Token 的完整步骤、密钥管理、过期时间设置
JWT 的生成与签发是分布式鉴权的核心环节。服务端在用户身份验证通过后,需要按照标准流程构造 JWT 载荷、选择签名算法、管理密钥并设置合理的过期时间。本章将拆解这一完整链路,并深入分析密钥管理与过期策略对系统性能和安全性的影响。
4.1 服务端生成 Token 的完整步骤
JWT 的生成过程可归纳为以下 6 个核心步骤,每一步都直接影响 Token 的安全性与解析效率:
- 构造 Header(头部):声明 Token 类型(
typ: JWT)和签名算法(alg),如HS256或RS256。 - 构造 Payload(载荷):包含注册声明(
iss、sub、exp、iat等)、公共声明和私有声明(如用户 ID、角色)。 - 序列化 Header 与 Payload:分别对 JSON 对象进行 Base64URL 编码,得到
header和payload字符串。 - 生成签名(Signature):使用指定算法和密钥对
header.payload进行签名,输出签名值。 - 拼接完整 Token:将
header.payload.signature以点号(.)连接,形成最终 JWT 字符串。 - 返回给客户端:通常放在 HTTP 响应头
Authorization: Bearer <token>或响应体中返回。
性能要点: 步骤 1-3 为纯字符串操作,开销极低;步骤 4 的签名计算是唯一 CPU 密集型操作,其耗时取决于算法类型和密钥长度。对称算法(HS256)比非对称算法(RS256/ES256)快 2-5 倍。
4.2 密钥管理策略
密钥是 JWT 签名的安全基石。不同的签名算法对应不同的密钥管理模型,直接影响系统的安全边界和运维复杂度。
| 算法类型 | 密钥形式 | 管理复杂度 | 典型场景 |
|---|---|---|---|
| HS256(对称) | 单一共享密钥(256-bit) | 低 | 单体应用、内部微服务 |
| RS256(非对称) | 私钥签名 + 公钥验证 | 中 | 分布式系统、第三方鉴权 |
| ES256(非对称) | 私钥签名 + 公钥验证(椭圆曲线) | 中 | 移动端、IoT 设备 |
安全警告: 对称密钥(HS256)一旦泄露,攻击者可伪造任意 Token。在分布式系统中,共享密钥的传递和存储风险较高,建议优先使用非对称算法(RS256/ES256),并配合密钥轮换机制。
4.2.1 密钥轮换(Key Rotation)
定期更换密钥是降低泄露风险的关键手段。推荐使用 JWKS(JSON Web Key Set) 端点动态发布公钥,服务端在验证 Token 时根据 kid(Key ID)自动匹配当前有效的公钥。
// JWKS 端点返回示例(简化)
{
"keys": [
{
"kty": "RSA",
"kid": "key-2025-01",
"n": "0vx7ago...",
"e": "AQAB",
"alg": "RS256"
},
{
"kty": "RSA",
"kid": "key-2024-12",
"n": "1wz5fbc...",
"e": "AQAB",
"alg": "RS256"
}
]
}
最佳实践: 密钥轮换周期建议为 30-90 天。旧密钥在轮换后保留一段“宽限期”(如 24 小时),确保已签发但未过期的 Token 仍能被验证。
4.3 过期时间设置
exp(Expiration Time)是 JWT 中最重要的注册声明之一,它定义了 Token 的有效截止时间。过期时间的设置需要在安全性与用户体验之间取得平衡。
4.3.1 过期时间对性能的影响
较短的过期时间意味着客户端需要更频繁地刷新 Token,增加服务端签发请求的负载;较长的过期时间则增加了 Token 泄露后的风险窗口。下表展示了不同过期策略的性能与安全权衡:
| 过期策略 | 典型值 | 签发频率 | 安全风险 | 推荐场景 |
|---|---|---|---|---|
| 极短过期 | 5-15 分钟 | 高 | 低 | 金融、支付、高安全系统 |
| 中等过期 | 1-24 小时 | 中 | 中 | Web 应用、API 鉴权 |
| 长过期 | 7-30 天 | 低 | 高 | 移动端、IoT、低敏感场景 |
4.3.2 配合 Refresh Token 的过期模型
为平衡安全与体验,业界普遍采用 Access Token + Refresh Token 双 Token 模型:
- Access Token:短期有效(如 15 分钟),用于访问受保护资源,不存储敏感信息。
- Refresh Token:长期有效(如 7 天),仅用于获取新的 Access Token,存储在服务端或安全存储中。
性能优化点: Access Token 的过期时间越短,服务端验证时对黑名单的依赖越低(因为 Token 很快自动失效),从而减少分布式缓存或数据库的查询压力。建议将 Access Token 过期时间设为 15-30 分钟,配合 Refresh Token 实现无感续期。
4.4 JWT 生成与签发流程总览
下图展示了从用户登录到 JWT 签发的完整流程,以及密钥管理与过期时间在其中的作用位置:
4.5 生成签名的代码示例
以下代码展示了使用 Node.js 的 jsonwebtoken 库生成 RS256 签名的完整过程,包含密钥加载、过期时间设置和 Token 签发:
const jwt = require('jsonwebtoken');
const fs = require('fs');
// 1. 加载私钥(非对称算法)
const privateKey = fs.readFileSync('./keys/private.pem', 'utf8');
// 2. 构造 Payload,设置过期时间
const payload = {
sub: 'user_12345',
name: '张三',
role: 'admin',
iat: Math.floor(Date.now() / 1000), // 签发时间
exp: Math.floor(Date.now() / 1000) + 900 // 过期时间:15 分钟后
};
// 3. 生成签名并签发 Token
const token = jwt.sign(payload, privateKey, {
algorithm: 'RS256',
keyid: 'key-2025-01' // 用于 JWKS 公钥匹配
});
console.log('生成的 JWT:', token);
性能建议: 在生成 Token 时,避免在 Payload 中包含过大的自定义声明(如完整的用户对象)。Payload 越大,Base64URL 编码后的字符串越长,网络传输和存储开销也随之增加。建议仅包含必要的最小声明集(如用户 ID、角色、权限范围)。