4. JWT 生成与签发:服务端核心流程

JWT 的生成与签发是服务端认证体系的第一道关口。本节将深入解析 Token 的完整生成流程、过期时间策略以及自定义 Claims 的最佳实践,确保你能够构建安全、可扩展的认证签发服务。

4.1 服务端生成 Token 的标准流程

JWT 签发并非简单的字符串拼接,而是遵循严格的加密与编码步骤。下图展示了从用户认证通过到 Token 返回的完整链路:

JWT 服务端签发流程 ① 用户认证通过 ② 构建 Payload ③ 设置 Header ④ 签名运算 ⑤ 生成 Token ⑥ 返回客户端 ⚠ 关键安全点 签名密钥必须安全存储 禁止在 Payload 存放密码
核心要点:JWT 签发必须遵循“先构建、后签名”的顺序。任何对 Payload 或 Header 的修改都需要重新计算签名,否则 Token 将被视为无效。

4.2 设置过期时间(exp)

过期时间(exp)是 JWT 中最重要的安全 Claims 之一。它定义了 Token 的有效截止时间,服务端必须严格校验该字段以防止 Token 被无限期使用。

4.2.1 过期时间策略对比

策略 过期时长 适用场景 安全等级
短期 Token 15 分钟 – 1 小时 高安全要求、金融、管理后台 高
中期 Token 1 小时 – 24 小时 普通 Web 应用、移动端 API 中
长期 Token 7 天 – 30 天 低敏感场景、IoT 设备 低
无过期(不推荐) 永久 禁止使用 极低
安全警告:切勿签发无过期时间的 JWT。一旦密钥泄露,攻击者可永久使用该 Token。建议短期 Token + Refresh Token 组合方案。

4.2.2 过期时间代码实现

以下示例展示了在 Node.js 中使用 jsonwebtoken 库设置过期时间的标准做法:

const jwt = require('jsonwebtoken');

// 用户认证通过后的用户信息
const userPayload = {
  userId: 'u_2024001',
  role: 'admin',
  department: 'engineering'
};

// 设置过期时间为 1 小时
const token = jwt.sign(
  userPayload,
  process.env.JWT_SECRET,
  {
    expiresIn: '1h',          // 支持 '1h'、'7d'、'30d' 等格式
    algorithm: 'HS256',       // 推荐使用 HS256 或 RS256
    issuer: 'my-api-server',  // 可选,标识签发者
    subject: userPayload.userId // 可选,标识用户
  }
);

console.log('Generated Token:', token);

4.3 自定义 Claims 的设计与规范

自定义 Claims 是 JWT 扩展能力的核心。通过合理设计自定义字段,可以在 Token 中携带业务所需的用户属性、权限标识或会话上下文。

4.3.1 标准 Claims 与自定义 Claims 对比

类别 Claim 名称 说明 是否必须
标准 Claims iss 签发者标识 推荐
sub 主题(通常为用户 ID) 推荐
exp 过期时间 必须
iat 签发时间 推荐
自定义 Claims role 用户角色(admin/user/guest) 按需
permissions 权限列表数组 按需
sessionId 会话唯一标识 按需

4.3.2 自定义 Claims 最佳实践

  • 最小化原则:只存放认证与授权必需的字段,避免存放用户密码、手机号等敏感信息。
  • 命名规范:使用驼峰命名(如 userRole)或下划线命名(如 user_role),保持团队统一。
  • 避免嵌套过深:Claims 应保持扁平结构,嵌套层级不超过 2 层。
  • 类型明确:字符串、数字、布尔值、数组均可,但避免使用复杂对象。

4.3.3 完整自定义 Claims 示例

const customClaims = {
  // 标准 Claims
  iss: 'auth-service',
  sub: 'u_2024001',
  exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1 小时后
  iat: Math.floor(Date.now() / 1000),

  // 自定义 Claims
  role: 'admin',
  permissions: ['read:users', 'write:users', 'delete:users'],
  sessionId: 'sess_abc123',
  department: 'engineering',
  isMfaVerified: true
};

const token = jwt.sign(customClaims, process.env.JWT_SECRET);
console.log('Custom Claims Token:', token);
设计建议:自定义 Claims 的命名建议添加应用前缀(如 app_role),避免与未来新增的标准 Claims 冲突。同时,所有自定义 Claims 的总大小应控制在 1KB 以内,以降低网络传输开销。

4.4 签发流程中的安全校验清单

在生成 Token 之前,服务端应完成以下校验,确保签发的 Token 安全可靠:

  1. 用户身份校验:确认用户名/密码、OAuth 令牌或生物特征验证通过。
  2. 用户状态检查:确认账户未被锁定、禁用或删除。
  3. 权限范围确认:根据用户角色确定可授权的 Claims 范围。
  4. 过期时间合理性:根据安全策略设置合适的 exp 值。
  5. 密钥安全加载:从环境变量或密钥管理服务读取签名密钥,禁止硬编码。