4. JWT Payload 设计:注册声明、公共声明与私有声明

JWT 的 Payload(载荷)是承载用户身份信息与元数据的核心部分。它由三组声明(Claims)构成:注册声明(Registered Claims)、公共声明(Public Claims)和私有声明(Private Claims)。理解这三者的职责边界与使用规范,是设计安全、可扩展的 JWT 接口契约的前提。

4.1 注册声明(Registered Claims)

注册声明是 JWT 标准(RFC 7519)预定义的一组可选但强烈建议使用的声明。它们提供了令牌生命周期管理与安全校验的基础能力。以下为七个核心注册声明字段:

声明名 全称 类型 说明 校验要求
iss Issuer String 或 URI 签发该 JWT 的实体标识,通常为服务域名或应用 ID 接收方必须验证 iss 是否与信任的签发方一致
sub Subject String 或 URI JWT 所面向的主体,通常是用户 ID 或设备 ID 接收方应确保 sub 在签发方范围内唯一
aud Audience String 或 Array JWT 的接收方标识,用于限定令牌的使用目标 接收方必须验证自身是否在 aud 列表中
exp Expiration Time NumericDate 令牌的过期时间戳(秒),过期后令牌失效 接收方必须拒绝 exp 已过的令牌
nbf Not Before NumericDate 令牌生效时间戳,在此之前令牌不可用 接收方应拒绝当前时间早于 nbf 的令牌
iat Issued At NumericDate 令牌签发时间戳,用于计算令牌年龄 接收方可记录 iat 用于防重放或日志审计
jti JWT ID String 令牌唯一标识符,用于防止重放攻击 接收方应维护已用 jti 黑名单,拒绝重复使用
设计建议:在接口契约中,exp 和 iss 应作为强制校验字段。jti 在高安全场景(如支付、敏感操作)中必须启用,配合服务端黑名单实现一次性令牌。

4.2 公共声明(Public Claims)

公共声明是使用 IANA JSON Web Token Claims 注册表或通过 URI 定义的声明,用于在开放生态中避免命名冲突。任何组织或个人都可以向 IANA 注册新的公共声明,或使用包含命名空间的 URI 作为声明名。

  • IANA 注册声明: 如 name、email、picture 等,已被广泛接受,可直接使用。
  • URI 命名空间声明: 格式为 https://example.com/claims/role,确保全局唯一性。
  • 使用原则: 公共声明应优先使用 IANA 已注册的字段;自定义字段必须使用 URI 前缀,避免与未来标准冲突。
示例: 使用 URI 命名空间的公共声明
{
  "iss": "https://auth.example.com",
  "sub": "user_12345",
  "aud": "https://api.example.com",
  "exp": 1718000000,
  "https://example.com/claims/role": "admin",
  "https://example.com/claims/permissions": ["read", "write"]
}

4.3 私有声明(Private Claims)

私有声明是 JWT 签发方与接收方之间通过双边协商定义的声明,不依赖任何注册表或 URI。它们仅在特定服务或应用内部有效,不对外公开。

  • 适用场景: 内部微服务间传递用户会话状态、临时业务属性、设备指纹等。
  • 命名规范: 建议使用简短、有业务含义的键名,如 user_role、session_id、tenant_id。
  • 安全约束: 私有声明不应包含敏感信息(如密码、密钥),因为 Payload 仅 Base64 编码,未加密。
重要提醒: 私有声明必须通过接口契约文档明确约定,包括字段名、类型、取值范围、可选/必选状态。缺少契约的私有声明会导致接收方解析失败或安全漏洞。

4.4 三类声明的对比与选择

维度 注册声明 公共声明 私有声明
标准化程度 RFC 标准,全局通用 IANA 注册或 URI 命名 双边约定,无标准
命名冲突风险 低(标准定义) 低(URI 保证唯一) 高(需内部协调)
跨系统互操作性 高 中(需解析 URI) 低(仅限约定双方)
典型用途 令牌生命周期、签发方、主体 用户公开信息、角色 内部业务属性、临时状态
推荐使用 必须包含 iss、exp、sub 跨服务共享时使用 内部微服务间使用

4.5 Payload 声明结构总览

下图展示了 JWT Payload 中三类声明的组织关系与典型字段分布:

JWT Payload 声明结构 注册声明 (RFC 7519 标准) iss: 签发方 sub: 主体 aud: 接收方 exp: 过期时间 nbf: 生效时间 iat: 签发时间 jti: 令牌 ID 公共声明 (IANA / URI 命名) name: 用户姓名 email: 邮箱 picture: 头像 URL https://ex.com/role https://ex.com/dept ... 私有声明 (双边协商) user_role: admin session_id: abc123 tenant_id: t-001 device_fp: fp_xyz login_ip: 10.0.0.1 ...

4.6 完整 Payload 契约示例

以下是一个符合接口契约规范的 JWT Payload 示例,包含三类声明的典型组合:

{
  "iss": "https://auth.example.com",
  "sub": "user_98765",
  "aud": ["https://api.example.com", "https://admin.example.com"],
  "exp": 1718000000,
  "nbf": 1717996400,
  "iat": 1717992800,
  "jti": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "张三",
  "email": "zhangsan@example.com",
  "https://example.com/claims/role": "editor",
  "user_role": "editor",
  "tenant_id": "t-002",
  "session_id": "sess_abc123"
}
契约检查清单:
  • 是否包含 iss 且与签发方一致?
  • sub 是否在签发方范围内唯一?
  • aud 是否包含当前服务的标识?
  • exp 是否已校验且未过期?
  • 私有声明是否在接口文档中明确定义?
  • 是否避免在 Payload 中存放敏感信息?