4. JWT Payload 详解:注册声明、公共声明、私有声明

JWT 的 Payload(载荷)是令牌中承载实际数据的部分,它是一个 JSON 对象,包含一组声明(Claims)。声明是关于实体(通常是用户)和附加数据的陈述。根据 JWT 标准(RFC 7519),声明分为三种类型:注册声明、公共声明和私有声明。理解这三者的边界与用途,是设计安全、可互操作的 JWT 认证授权系统的核心。

4.1 注册声明(Registered Claims)

注册声明是一组预定义的、非强制性的但推荐使用的声明。它们提供了互操作性基础,确保不同系统之间对某些关键字段有一致的理解。这些声明通常用于控制令牌的生命周期和标识。

声明名 全称 类型 说明
iss Issuer 字符串或 URL 签发该 JWT 的实体。通常是服务端的唯一标识,如 https://auth.example.com。接收方应验证 iss 是否与期望的签发者一致。
sub Subject 字符串 JWT 所描述的主体。通常是用户的唯一标识(如 user_id)。该声明在签发者范围内必须是全局唯一的。
aud Audience 字符串或数组 JWT 的接收方。标识此令牌 intended for 哪个服务。如果接收方不在 aud 列表中,应拒绝该令牌。
exp Expiration Time 数字(NumericDate) 过期时间。在此时间之后,JWT 不能再被接受处理。这是一个绝对时间戳(Unix 时间戳)。
nbf Not Before 数字(NumericDate) 生效时间。在此时间之前,JWT 不能被接受处理。用于延迟令牌生效。
iat Issued At 数字(NumericDate) 签发时间。标识 JWT 是什么时候被创建的。
jti JWT ID 字符串 令牌的唯一标识符。用于防止重放攻击(Replay Attack)。服务端可以维护一个已使用 jti 的集合。
最佳实践: 始终验证 exp 和 iss。对于跨服务调用的场景,务必验证 aud。对于高安全性场景,使用 jti 并配合服务端缓存实现一次性令牌。

4.2 公共声明(Public Claims)

公共声明是由 IANA JSON Web Token Claims 注册表定义的,或者使用 URI(统一资源标识符)定义的声明,以避免命名冲突。这些声明通常用于传递标准化的、非敏感的用户属性或上下文信息。

  • 标准化公共声明示例: name、email、picture、preferred_username 等。这些字段在 OpenID Connect 标准中被广泛使用。
  • 自定义公共声明: 如果你的组织需要定义一个新的声明,并且希望它能在多个系统间共享,建议使用一个包含命名空间的 URI 作为声明名,例如 https://yourcompany.com/claims/employee_id。
安全警告: 公共声明通常包含用户个人信息(PII)。切勿将密码、密钥、完整的信用卡号等敏感数据放入 JWT 的 Payload 中。JWT 的 Payload 默认只经过 Base64 编码,并未加密,任何拥有令牌的人都可以解码读取。

4.3 私有声明(Private Claims)

私有声明是 JWT 的签发者和接收者之间私下约定的声明。它们既不是注册声明,也不是公共声明。使用私有声明时,双方必须事先就声明名称和值的含义达成一致。

  • 典型用途: 传递应用特定的业务数据,如用户角色(role)、权限列表(permissions)、部门 ID(dept_id)等。
  • 命名冲突风险: 由于没有全局注册机制,私有声明名应尽量具体,避免使用过于通用的名称(如 data、info),以防止与未来的注册声明或公共声明冲突。

4.4 三种声明的对比与选择

特性 注册声明 公共声明 私有声明
标准化程度 高(RFC 标准) 中(IANA 注册或 URI) 低(双方约定)
命名冲突风险 低 低(使用 URI 时) 高
互操作性 强 较强 弱(仅限约定双方)
典型用途 生命周期控制、标识 标准化用户信息 应用特定业务数据

4.5 Payload 结构示例

以下是一个包含注册声明、公共声明和私有声明的完整 JWT Payload 示例:

{
  // 注册声明
  "iss": "https://auth.myapp.com",
  "sub": "user_1234567890",
  "aud": ["https://api.myapp.com", "https://admin.myapp.com"],
  "exp": 1718000000,
  "nbf": 1717996400,
  "iat": 1717996400,
  "jti": "unique-token-id-abc123",

  // 公共声明(标准化)
  "name": "张三",
  "email": "zhangsan@example.com",

  // 私有声明(应用特定)
  "role": "admin",
  "permissions": ["read:users", "write:users", "delete:users"],
  "dept_id": "DEPT-IT-001"
}

4.6 Payload 声明分类架构图

下图展示了 JWT Payload 中三类声明的分类、来源及其在令牌中的位置关系:

JWT Payload 声明分类架构图 JWT Payload 声明分类架构 JWT Payload 注册声明 iss, sub, aud exp, nbf, iat jti 公共声明 name, email picture, locale URI 自定义声明 私有声明 role, permissions dept_id, tenant_id 业务特定数据 关键原则 1. 注册声明:控制令牌生命周期与标识,必须严格验证。 2. 公共声明:传递标准化用户信息,避免敏感数据。 3. 私有声明:用于业务逻辑,需双方约定,注意命名冲突。

4.7 声明验证流程

接收方在解析 JWT 时,应按照以下流程验证 Payload 中的声明,以确保令牌的合法性和有效性:

  1. 验证签名: 确保 JWT 未被篡改(在验证 Header 和 Payload 之前,这是第一步)。
  2. 验证 iss: 检查签发者是否在受信任的签发者列表中。
  3. 验证 aud: 检查当前服务是否在令牌的接收方列表中。
  4. 验证 exp: 检查当前时间是否已超过过期时间。
  5. 验证 nbf: 如果存在,检查当前时间是否在生效时间之后。
  6. 验证 jti: 如果启用了防重放,检查该 jti 是否已被使用。
  7. 提取业务声明: 验证通过后,提取公共声明和私有声明用于授权决策。
核心要点: Payload 是 JWT 的“数据面”,其设计直接决定了系统的安全性和可扩展性。注册声明是骨架,公共声明是标准接口,私有声明是业务血肉。三者结合,构成了 JWT 认证授权契约的完整数据边界。