4、租户身份与认证:OIDC/OAuth2.0多租户扩展、租户上下文传递、JWT租户声明

在多租户平台中,身份与认证是隔离的第一道防线。本章聚焦于如何基于 OIDC/OAuth2.0 协议实现多租户扩展,确保租户上下文在微服务间正确传递,并通过 JWT 声明承载租户标识,为后续的故障域隔离与路由决策提供可信依据。

4.1 OAuth2.0 / OIDC 多租户扩展模型

标准 OAuth2.0 协议并未原生定义“租户”概念。多租户扩展的核心在于将 issuer(签发者) 与 audience(受众) 与租户标识绑定。常见扩展方式有三种:

扩展方式 原理 适用场景 隔离粒度
独立 Issuer 模式 每个租户拥有独立的 OIDC Issuer URL(如 https://auth.tenant-a.example.com) 强隔离、合规要求高的金融/医疗租户 最高
路径/子域模式 共享 Issuer,通过 URL 路径或子域区分租户(如 https://auth.example.com/tenant-a) SaaS 多租户平台,租户数量中等 中
Claim 注入模式 共享 Issuer,在 的 tenant_id 或 org_id 声明中标识租户 轻量级多租户,租户数量大且动态 低(依赖验证逻辑)
推荐实践: 在平台门户中,优先采用 路径/子域模式 + Claim 注入 的组合。Issuer 保持统一,便于证书管理和客户端配置;同时通过 JWT 中的 tenant_id 声明实现细粒度路由。

4.2 租户上下文传递机制

用户认证通过后,租户上下文必须沿请求链路透明传递,避免每个服务重复解析 。传递机制分为 边界传递 和 内部传递 两层:

  • 边界传递(Ingress/Gateway): API 网关解析 JWT,提取 tenant_id,将其注入 HTTP Header(如 X-Tenant-ID)。
  • 内部传递(微服务间): 使用 gRPC Metadata 或 HTTP Header 透传,服务框架(如 Istio/Envoy)通过自定义 Filter 自动注入。
  • 异步传递(消息队列): 在消息头中携带租户上下文,消费者消费时重建租户环境。
关键原则: 租户上下文必须作为 不可篡改的请求属性 传递,禁止从用户输入或 URL 参数中读取租户标识,防止租户越权。
租户上下文传递流程 用户 API 网关 解析 JWT X-Tenant-ID 服务 A 读取 Header gRPC Metadata 服务 B 租户隔离 传递规则: 1. 网关必须验证 JWT 签名后,才提取 tenant_id 注入 Header。 2. 内部服务间传递时,禁止重新解析 JWT,应直接透传 Header/Metadata。 3. 异步消息需在消息属性中携带租户上下文,消费者端自动恢复。

4.3 JWT 租户声明设计

JWT 是承载租户上下文的核心载体。标准声明(sub, iss, aud)之外,需要自定义租户相关声明。推荐使用 扁平结构 而非嵌套结构,以降低解析复杂度。

{
  "iss": "https://auth.platform.example.com",
  "sub": "user-12345",
  "aud": "portal-api",
  "exp": 1718000000,
  "iat": 1717996400,
  // 租户声明(自定义)
  "tenant_id": "tenant-abc-001",
  "tenant_name": "金融事业部",
  "tenant_tier": "premium",
  "roles": ["admin", "operator"],
  "permissions": ["namespace:read", "deployment:create"]
}
声明字段 类型 说明 是否必选
tenant_id string 租户唯一标识,用于路由和隔离 是
tenant_name string 租户可读名称,用于审计和展示 推荐
tenant_tier string 租户服务等级(如 basic/premium/enterprise) 推荐
roles array 用户在租户内的角色列表 推荐
permissions array 细粒度权限标识,用于 RBAC 决策 可选
安全警告: JWT 的 payload 是 Base64 编码而非加密,切勿在声明中放置敏感信息(如数据库密码、)。租户上下文仅用于路由和权限校验,敏感配置应通过 Secret 管理服务获取。

4.4 多租户认证流程(含故障切换)

当认证服务或租户域发生故障时,平台需要具备优雅降级能力。以下流程整合了正常认证与故障切换逻辑:

  1. 客户端请求:用户携带 Authorization Header 访问平台门户。
  2. 网关拦截:API 网关验证 JWT 签名(使用 JWKS 端点获取公钥)。
  3. 租户路由:提取 tenant_id,查询租户路由表,确定目标服务集群。
  4. 故障检测:若目标集群不可用,网关根据 tenant_tier 决定是否切换到备用集群(premium 租户自动切换,basic 租户返回 503)。
  5. 上下文注入:将 X-Tenant-ID 和 X-Tenant-Tier 注入请求头,转发至后端服务。
多租户认证与故障切换流程 ① 客户端 ② 网关验证 JWT 签名 + JWKS ③ 租户路由 查询路由表 ④ 故障检测 集群健康检查 不可用 故障决策 premium → 备用集群 basic → 返回 503 ⑤ 注入 Header → 转发

4.5 最佳实践与避坑指南

缓存与刷新: 网关应缓存 JWKS 公钥(TTL 建议 1 小时),避免每次请求都拉取。同时,JWT 的 exp 不宜过长(建议 15-30 分钟),配合 Refresh 实现无感续期。
  • 租户 ID 不可变: 一旦分配,租户 ID 不得变更。如需重命名,使用 tenant_name 字段,tenant_id 保持稳定。
  • 避免 Header 污染: 内部服务间传递时,使用 X-Tenant-* 命名空间,避免与业务 Header 冲突。
  • 审计日志: 所有认证和授权决策必须记录 tenant_id、user_id、action 和 result,便于故障排查。
  • 故障切换幂等: 切换至备用集群时,确保 JWT 的 aud 声明包含备用集群的标识,避免 被拒绝。