4. JWT 的 Payload 设计:标准声明、自定义声明与安全注意事项

JWT 的 Payload(载荷)是令牌中承载实际数据的部分,其设计质量直接决定了认证系统的安全性、可扩展性与可维护性。Payload 由一组声明(Claims)组成,分为注册声明(标准声明)、公共声明和私有声明(自定义声明)三类。本章将深入解析 Payload 的结构设计原则与安全实践。

4.1 标准声明(Registered Claims)

标准声明是 JWT 规范(RFC 7519)预定义的一组可选字段,用于提供令牌的元数据。虽然这些字段并非强制要求,但在生产环境中强烈建议按需使用,以增强令牌的可控性和互操作性。

声明名 全称 类型 说明 使用建议
iss Issuer 字符串 签发者,标识签发 JWT 的实体(如认证服务器 URL) 必填,用于多服务间验证令牌来源
sub Subject 字符串 主题,标识 JWT 所面向的用户或实体(如用户 ID) 必填,作为用户身份的唯一标识
aud Audience 字符串或数组 接收方,标识此 JWT 的目标服务或资源 多服务架构中必填,防止令牌被滥用
exp Expiration Time 数字(Unix 时间戳) 过期时间,在此时间之后令牌无效 必填,建议设置较短有效期(15-30 分钟)
nbf Not Before 数字(Unix 时间戳) 生效时间,在此时间之前令牌不可用 可选,用于延迟生效场景(如定时任务授权)
iat Issued At 数字(Unix 时间戳) 签发时间,标识令牌的生成时刻 推荐使用,配合 exp 计算剩余有效期
jti JWT ID 字符串 令牌唯一标识,用于防止重放攻击 高安全场景推荐,需配合服务端黑名单
最佳实践:在异步任务权限验证场景中,aud 声明尤为重要。建议将任务队列名称或任务类型编码到 aud 中,确保一个令牌只能用于执行特定类型的任务,防止令牌被跨任务滥用。

4.2 自定义声明(Custom Claims)

自定义声明用于携带业务相关的数据,分为公共声明和私有声明两类。公共声明需要在 IANA JSON Web Token Claims 注册表中注册,或使用命名空间避免冲突;私有声明由服务双方协商定义,不对外公开。

4.2.1 常见自定义声明设计模式

  • 用户角色与权限:"role": "admin"、"permissions": ["read:task", "write:task"]
  • 会话上下文:"session_id": "abc123"、"client_ip": "192.168.1.1"
  • 异步任务上下文:"task_type": "report_generation"、"max_execution_seconds": 300
  • 资源访问范围:"scope": "project:42"、"allowed_queues": ["high", "default"]
异步任务权限验证示例 Payload:
{
  "iss": "https://auth.example.com",
  "sub": "user_10086",
  "aud": "task_queue:report",
  "exp": 1718000000,
  "iat": 1717999100,
  "jti": "unique-token-id-001",
  "task_type": "report_generation",
  "allowed_queues": ["high_priority"],
  "max_retries": 3,
  "scope": "project:42"
}

4.3 Payload 安全注意事项

Payload 仅经过 Base64 编码,并未加密。任何持有令牌的人都可以解码查看 Payload 内容。因此,以下安全原则必须严格遵守:

4.3.1 禁止存放敏感信息

  • 绝对禁止:密码、信用卡号、身份证号、个人医疗信息等敏感数据
  • 避免存放:完整的用户个人信息(如手机号、邮箱地址),除非经过脱敏处理
  • 推荐做法:仅存放用户 ID 或会话标识,敏感数据通过服务端数据库查询获取

4.3.2 声明验证策略

声明 验证策略 常见攻击场景
iss 必须与预期签发者完全匹配 伪造签发者绕过认证
aud 必须包含当前服务的标识 令牌重放到其他服务
exp 当前时间必须小于 exp 值 使用过期令牌重放攻击
nbf 当前时间必须大于等于 nbf 值 提前使用未生效令牌
jti 检查是否在黑名单中 令牌重放攻击

4.3.3 Payload 大小控制

JWT 通常通过 HTTP Header 传输,过大的 Payload 会导致网络开销增加。建议将 Payload 大小控制在 1KB 以内。对于需要携带大量权限信息的场景,考虑使用引用方式(如权限 ID 列表)而非完整权限对象。

安全红线:永远不要信任 Payload 中的任何数据。所有声明在服务端验证签名后,仍需进行业务逻辑校验。例如,即使 Payload 中声明 "role": "admin",服务端也必须验证该用户确实拥有管理员权限。

4.4 Payload 结构设计流程

下图展示了从需求分析到 Payload 生成的完整设计流程,帮助你在实际项目中系统化地构建安全的 Payload。

JWT Payload 设计流程 1. 需求分析 2. 选择标准声明 3. 设计自定义声明 4. 安全检查 5. 生成 Payload 关键检查项 • 是否包含 iss/sub/exp? • aud 是否匹配目标服务? • 自定义声明是否最小化? • 是否包含敏感信息? • Payload 大小是否 < 1KB? • jti 是否需要防重放? • nbf 是否需要延迟生效? • 权限声明是否可被篡改? • 是否验证签名后再信任? • 过期时间是否合理? • 是否记录签发日志? • 是否支持令牌撤销?

4.5 异步任务场景下的 Payload 设计要点

在异步任务权限验证中,Payload 设计需要额外关注以下方面:

  • 任务粒度权限:使用 task_type 和 allowed_queues 声明,精确控制 Worker 可执行的任务类型
  • 资源隔离:通过 scope 声明限定任务可操作的资源范围(如项目 ID、租户 ID)
  • 执行约束:使用自定义声明限制任务的最大执行时间、重试次数等运行时参数
  • 审计追踪:在 Payload 中包含 request_id 或 trace_id,便于链路追踪和问题排查
异步任务 Payload 设计原则:尽量将任务执行所需的上下文信息编码到 Payload 中,减少 Worker 对数据库的查询依赖。但需注意 Payload 大小限制,避免携带过多冗余数据。