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 中三类声明的组织关系与典型字段分布:
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 中存放敏感信息?