4. JWT 生成与签发流程:服务端生成 Token 的完整步骤、密钥管理、过期时间设置

JWT 的生成与签发是分布式鉴权的核心环节。服务端在用户身份验证通过后,需要按照标准流程构造 JWT 载荷、选择签名算法、管理密钥并设置合理的过期时间。本章将拆解这一完整链路,并深入分析密钥管理与过期策略对系统性能和安全性的影响。

4.1 服务端生成 Token 的完整步骤

JWT 的生成过程可归纳为以下 6 个核心步骤,每一步都直接影响 Token 的安全性与解析效率:

  1. 构造 Header(头部):声明 Token 类型(typ: JWT)和签名算法(alg),如 HS256 或 RS256。
  2. 构造 Payload(载荷):包含注册声明(iss、sub、exp、iat 等)、公共声明和私有声明(如用户 ID、角色)。
  3. 序列化 Header 与 Payload:分别对 JSON 对象进行 Base64URL 编码,得到 header 和 payload 字符串。
  4. 生成签名(Signature):使用指定算法和密钥对 header.payload 进行签名,输出签名值。
  5. 拼接完整 Token:将 header.payload.signature 以点号(.)连接,形成最终 JWT 字符串。
  6. 返回给客户端:通常放在 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 签发的完整流程,以及密钥管理与过期时间在其中的作用位置:

JWT 生成与签发流程 JWT 生成与签发流程 用户登录 / 身份验证 构造 Header (alg, typ) 构造 Payload (含 exp) Base64URL 编码 生成签名 (Signature) 拼接完整 JWT 密钥管理 对称 / 非对称密钥 密钥轮换 (JWKS) kid 标识 过期时间设置 exp 声明 Access / Refresh Token 过期策略权衡

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、角色、权限范围)。