4. JWT 生成与签发:服务端核心流程
JWT 的生成与签发是服务端认证体系的第一道关口。本节将深入解析 Token 的完整生成流程、过期时间策略以及自定义 Claims 的最佳实践,确保你能够构建安全、可扩展的认证签发服务。
4.1 服务端生成 Token 的标准流程
JWT 签发并非简单的字符串拼接,而是遵循严格的加密与编码步骤。下图展示了从用户认证通过到 Token 返回的完整链路:
核心要点: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 安全可靠:
- 用户身份校验:确认用户名/密码、OAuth 令牌或生物特征验证通过。
- 用户状态检查:确认账户未被锁定、禁用或删除。
- 权限范围确认:根据用户角色确定可授权的 Claims 范围。
- 过期时间合理性:根据安全策略设置合适的
exp值。 - 密钥安全加载:从环境变量或密钥管理服务读取签名密钥,禁止硬编码。