多租户身份与租户识别策略:租户上下文传递、租户解析中间件、跨域身份映射
边界定义:本章聚焦多租户SaaS架构中的身份边界与租户识别域。认证(Authentication)确认用户身份,租户识别(Tenant Resolution)则决定用户在哪个租户范围内操作。两者构成IAM(身份与访问管理)前两层,任何一层被突破都会导致跨租户数据越权。信任边界从外部请求到达接入层开始,终止于下游服务读取租户上下文的点。
分层架构与核心职责
| 架构层 | 组件 | 职责 |
|---|---|---|
| 接入层 | 负载均衡 / TLS 卸载 | 识别目标域名,处理 SSL 终止,转发原始请求头 |
| 网关层 | 反向代理 / API 网关 | 认证令牌校验,提取租户标识并将其注入自定义请求头 |
| 中间件层 | 租户解析中间件 | 从 Host / Path / JWT Claims 中解析租户 Slug 或 ID |
| 上下文层 | 租户上下文管理器 | 存储当前请求的租户信息,向下游业务服务透明传递 |
| 业务层 | 核心微服务 | 依赖上下文进行租户级数据路由和隔离策略执行 |
核心策略方案详解
1. 租户上下文传递
在请求链路中持续传递租户标识。常见模式:
- 线程本地变量(ThreadLocal):适用于同步阻塞模型,需注意线程池复用导致上下文泄漏。
- 请求上下文:GraphQL Context、gRPC Metadata、HTTP Header(
X-Tenant-ID),显式传递,安全可控。 - JWT Claims:在令牌中携带
tid、org_slug等声明,实现无状态租户传递。
// JWT Payload 示例
{
"sub": "user_uuid_12345",
"tid": "acme-corp",
"roles": ["admin", "billing_manager"]
}
2. 租户解析中间件
| 解析方式 | 典型实现 | 优劣 |
|---|---|---|
| 基于域名 | 读取 Host 头,映射 tenantA.example.com | 强制隔离性强,但依赖 DNS 和泛域名证书 |
| 基于路径 | 从 /{tenant_slug}/api/v1 提取第一段路径 | 部署简单,适合全托管 SaaS;路径重写易出错 |
| 基于请求头 | 自定义 Header X-Tenant-Id | 纯 RESTful,适合 B2B API 集成;客户端需配合 |
| 基于令牌声明 | 从 Bearer JWT 的 aud / iss / tid 字段解析 | 无状态,零配置改动;令牌过期需重新映射 |
3. 跨域身份映射
当租户使用外部身份提供商(IdP:Azure AD、Okta、自建 IdP)时,需将外部用户/组映射到内部租户和角色。核心模式:
- JIT(Just-In-Time)预配:首次登录时自动创建用户和租户关联,用户组通过 IdP 属性映射。
- SCIM 2.0 标准同步:从 IdP 同步用户与群组到 SaaS 系统,实时更新租户成员关系。
- 外部 JWT 映射:信任外部令牌的
tid声明,通过白名单或群体映射表建立互信。
风险提示:跨域映射一定要进行群体(Group)层级映射,避免直接映射个人账号,否则会极大增加租户数据泄露风险和运维复杂度。
数据流与控制流
控制流路径:客户端请求 → API 网关(认证) → 租户解析中间件 → 上下文注入 → 业务逻辑编排 → 数据层查询。
数据流约束:所有持久化查询必须携带租户上下文作为隐式过滤条件(WHERE tenant_id = ?),不允许上层自行拼接。
信任链路核心:TLS 终止 → 网关 JWT/OIDC 验证 → 租户声明提取并签名 → 服务间 mTLS 或 Token Exchange → 数据层查询过滤器。任何一环的信任失败都会导致租户边界失效。
选型权衡
| 维度 | 域名解析 | 路径解析 | Header/声明解析 |
|---|---|---|---|
| DNS 配置成本 | 高(泛域名、多证书) | 低 | 低 |
| 证书管理 | 复杂(SNI 证书) | 简单 | 简单 |
| API 客户端适配 | 自动适配 | 易适配 | 需修改客户端 |
| 租户隔离速度 | 快(连接级) | 慢(应用层重写) | 中(需解析 Header) |
| 适用场景 | 单租户规格,强隔离 | 标准多租户 SaaS | B2B API / 联邦认证 |
风险点与防范
- 租户标识泄露:错误地将 X-Tenant-ID 暴露在客户端日志里。使用加密 JWT Claims 传递,避免明文 Header。
- 线程池上下文污染:异步处理(如 Spring @Async)不继承父线程的 ThreadLocal。使用请求级上下文容器(如 ContextSnapshot)显式传递。
- 跨域映射欺诈:恶意用户尝试伪装成其他租户的 IdP 组。校验外部令牌的 Issuer 和 Audience,静态配置信任锚点。
- DNS 重新绑定攻击:基于域名的方案需配置严格的主机头白名单和 TLS 验证。
落地执行步骤
- 选定租户标识模式:使用 UUID(不可枚举)或 Slug(可读性高,如
acme-corp)。 - 标准化传递协议:统一使用请求头或 JWT Claims 传递租户 ID,确保语言无关。
- 开发/配置解析中间件:Kong 自定义插件 / Spring Cloud Gateway Filter / APISIX 租户路由插件。
- 强制校验机制:中间件不能解析出合法租户时直接返回 401/403,不允许降级为公共租户。
- 集成外部 IdP:配置 OIDC 发现端点和 SCIM 钩子,建立组映射规则。
- 自动化测试覆盖:编写跨租户访问测试用例,确保各组件正确传递上下文。
- 生产监控:对未携带租户上下文的请求进行告警,并在 Trace 中注入租户标签。
验收标准
- 100% 的 API 请求在进入业务逻辑前携带了有效的租户上下文。
- 不存在任何接口可以绕过租户上下文直接进行数据访问。
- 跨域身份映射延迟 <100ms(基于缓存场景)。
- 租户解析中间件在峰值 10000 QPS 下无泄漏、无长 GC。
- 安全渗透测试中,跨租户越权攻击全部被拦截并告警。
生产建议:在 JWT 中同时颁发 tenant_id 和 tenant_slug。ID 用于内部关联,Slug 用于日志归因和故障排查,两者配合可以有效减缓标识变更迁移带来的问题。