3. 多租户架构分层:租户识别层、认证层、授权层、数据隔离层、审计层
3.1 本章边界
多租户权限架构的核心挑战在于:一套身份与权限系统同时服务 N 个租户,每个租户的数据、角色、资源必须互相隔离,且租户管理员能独立管理本租户内的授权。本章定义的分层模型将权限系统拆解为五个职责明确的纵向层级,每个层级解决一个独立问题,并通过清晰的接口契约连接上下游。该模型不依赖特定编程语言或中间件,可映射至云原生环境、单体应用或微服务架构。
【分层边界】本章关注的“层”是逻辑隔离的组件,不是物理进程。同一层内的组件可以分布在多个服务中,但只要其行为符合该层的接口契约,仍视为同一层。
3.2 五层职责全景
| 层序号 | 层名称 | 核心职责 | 输入 | 输出 |
|---|---|---|---|---|
| 1 | 租户识别层 | 从请求中提取租户标识,校验租户有效性 | HTTP 请求 / 消息元数据 | 租户 ID(tenant_id) |
| 2 | 认证层 | 验证用户身份凭证,颁发会话 / token | 用户凭证 + 租户 ID | 用户身份断言(subject) |
| 3 | 授权层 | 基于 RBAC 策略判定用户是否有权限执行操作 | 用户身份 + 操作 + 资源路径 | 允许 / 拒绝决策 |
| 4 | 数据隔离层 | 按租户 ID 过滤 / 分区存储数据,确保数据不跨租户泄露 | SQL / API 请求 + 租户 ID | 隔离后的数据结果 |
| 5 | 审计层 | 记录所有身份与权限变更、关键操作日志 | 系统内部事件 | 不可篡改的审计记录 |
3.3 组件职责详解
3.3.1 租户识别层
该层是所有请求的第一道关卡。常见的租户识别策略包括:
- 域名/子域名:如
https://tenant-a.example.com - 请求路径前缀:如
https://api.example.com/tenant-a/users - HTTP Header(如 X-Tenant-ID):适合 API 调用
- JWT 自定义声明:颁发 token 时嵌入租户 ID
该层必须校验租户 ID 是否属于已激活租户,并拒绝非法或已停用租户的请求。如果租户识别失败,应返回 400 或 403,且不进入认证层。
3.3.2 认证层
认证层负责校验用户身份与租户归属的绑定关系。在多租户场景中:
- 同一用户名可能在不同租户下对应不同用户(如 user@tenant-a 与 user@tenant-b)。
- 支持 SSO(单点登录)时,认证层需与 IdP 交互,并确保返回的断言包含 tenant_id。
- 认证通过后,认证层生成包含 tenant_id、user_id、roles 的 token(JWT 或自签 token),供后续层使用。
【设计建议】认证层应维护一份“租户-身份存储映射表”,用于快速定位用户库所在的分区(如不同的 LDAP 域或数据库 schema)。
3.3.3 授权层
授权层是 RBAC 策略的核心执行点。其职责包括:
- 从认证层获取用户身份(含 roles)。
- 从请求上下文中提取操作(action)和资源(resource)。
- 查询当前租户下的角色-权限映射关系(注意:RBAC 策略是租户隔离的,每个租户独立管理)。
- 返回
permit或deny。
授权层不直接操作租户数据,仅读取策略存储。策略存储可以是关系数据库(RBAC 表)、PDP(如 OPA)或硬编码配置。
3.3.4 数据隔离层
该层是数据安全的最后防线。即使授权层误判,数据隔离层也应防止跨租户泄漏。常见实现模式:
| 模式 | 描述 | 适用场景 |
|---|---|---|
| 独立数据库 | 每个租户一个物理数据库实例 | 极端隔离要求、大型租户 |
| 共享数据库,独立 Schema | 同一实例,每个租户一个 schema | 中度隔离,管理成本适中 |
| 共享数据库,共享表 | 所有租户同一表,通过 tenant_id 列过滤 | 低成本,但需开发者严格保证查询中带上 tenant_id 条件 |
数据隔离层必须集成到 ORM/DAO 层、SQL 中间件或应用框架的查询拦截器中,在每条数据读写语句中自动拼接 tenant_id 过滤条件。
-- 示例:共享表模式下的安全查询
SELECT * FROM orders
WHERE tenant_id = 'tenant_a'
AND order_id = 12345;
3.3.5 审计层
审计层负责记录所有影响安全的事件,包括但不限于:
- 租户创建、停用、删除
- 用户登录成功/失败
- 角色与权限的变更
- 敏感资源的访问(如导出客户数据)
- 跨租户访问尝试(即使被拒绝)
审计日志应包含:时间戳、租户 ID、用户 ID、操作类型、目标资源、结果(成功/失败)、请求来源 IP。推荐使用不可篡改的存储(如写到独立日志服务或区块链锚定),并设置数据保留周期。
3.4 数据流 / 控制流 / 信任链路
下图展示一次典型请求经过五层的完整数据流与控制依赖关系:
3.5 选型权衡
| 维度 | 选项 | 优势 | 劣势 | 推荐场景 |
|---|---|---|---|---|
| 租户识别方式 | 域名 | 对用户透明,支持 CDN 自动路由 | 需备案多个域名,SSL 证书管理复杂 | B2C SaaS |
| 租户识别方式 | Header + JWT | 实现简单,易于 API 网关集中处理 | 需防止客户端伪造,必须 TLS | API 优先的应用 |
| 认证方案 | 自建用户库 | 完全可控,无外部依赖 | 需自建密码管理、多因素认证 | 私有部署 |
| 认证方案 | OIDC / SAML | 支持 SSO,降低密码泄露风险 | 依赖 IdP 可用性 | 企业级多租户 |
| 数据隔离模式 | 独立数据库 | 最强安全隔离,备份恢复独立 | 运维成本高,跨租户统计困难 | 金融、医疗 |
| 数据隔离模式 | 共享表 + RLS | 运维简单,按需伸缩 | 需确保每个查询都带租户过滤,易出错 | 中小规模 SaaS |
3.6 风险点
- 租户 ID 伪造:如果租户 ID 仅依赖请求中的 Header 或路径,而不在 token 中签名,恶意用户可构造请求访问其他租户数据。风险在租户识别层与认证层之间。
- 认证层与授权层信任链断裂:如果授权层直接接收 HTTP 请求属性而不校验 token 签名,攻击者可注入虚假身份。
- 数据隔离层遗漏:开发人员编写的 SQL 忘记带 tenant_id 条件,或 ORM 框架未强制注入过滤策略,导致数据泄露。
- 审计日志绕过:若审计层仅在授权允许后记录,则授权拒绝的恶意尝试将不可追溯,难以检测攻击前奏。
- 跨层状态不一致:例如租户在认证层被停用,但授权层仍缓存旧策略,导致已被停用的用户仍能操作。
【风险案例】2021 年某知名协作平台因共享表模式中 ORM 未强制注入 tenant_id,导致用户通过修改 API 参数访问其他企业的文档,影响数百万客户。事后该平台切换至独立 Schema + 查询拦截器方案。
3.7 落地步骤
- 定义租户识别策略:确定使用域名、路径还是 Header,并在 API 网关或中间件中统一提取 tenant_id。
- 实现认证层时绑定租户:用户注册时要求选择租户(或自动匹配),登录后 token 中必须包含 signed_tenant_id。
- 设计租户级 RBAC 存储:每个租户的角色表、权限表、用户-角色分配表都包含 tenant_id 字段,并建立复合主键 (tenant_id, id)。
- 集成授权决策点:选择 Policy Engine(如 OPA)或自建服务,将操作、资源、用户角色作为输入,输出 permit/deny。
- 实现数据隔离层:根据选型(共享表 / 独立 Schema / 独立数据库)配置 ORM 拦截器或数据库行级安全策略(RLS)。
- 搭建审计管道:定义审计事件结构,在关键业务代码中埋点异步发送至审计服务。保证审计日志独立于业务数据库。
- 端到端集成测试:构造跨租户访问测试用例,验证每层是否能正确拦截。
3.8 验收标准
| 验收项 | 预期结果 | 验证方式 |
|---|---|---|
| 租户 A 的用户无法访问租户 B 的数据 | 返回 403 或空结果 | 自动测试:模拟用户 A 请求租户 B 的资源 |
| 租户管理员只能管理本租户的角色 | 修改接口只影响本租户 | 手动测试 + 审计日志检查 |
| 认证失败请求不穿透到授权层 | 在认证层直接返回 401 | mock 认证层场景测试 |
| 审计日志可查询指定租户的最近 100 条记录 | 返回正确过滤的结果 | API 调用验证 |
| 租户停用后,该租户的所有 token 立即失效 | 下一请求被拒绝 | 模拟停用后验证 token 是否被黑名单拦截 |
【落地检查表】交付前应逐层检查:① 租户识别层是否在网关/中间件完成?② 认证层 token 是否包含 tenant_id 且签名?③ 授权层策略存储是否按租户分区?④ 数据隔离层是否在 ORM 层面自动注入 tenant_id?⑤ 审计日志是否包含租户字段且不可删除?