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 数据流 / 控制流 / 信任链路

下图展示一次典型请求经过五层的完整数据流与控制依赖关系:

多租户五层架构数据流与控制流 租户识别层 认证层 授权层 数据隔离层 数据存储 审计层 原始请求 tenant_id 身份断言 允许决策 过滤后查询 事件流(异步) 说明:实线表示同步调用(控制流),虚线表示异步事件(审计流)。授权层与数据隔离层之间的信任链路依赖于认证层签发的 token。

3.5 选型权衡

维度选项优势劣势推荐场景
租户识别方式域名对用户透明,支持 CDN 自动路由需备案多个域名,SSL 证书管理复杂B2C SaaS
租户识别方式Header + JWT实现简单,易于 API 网关集中处理需防止客户端伪造,必须 TLSAPI 优先的应用
认证方案自建用户库完全可控,无外部依赖需自建密码管理、多因素认证私有部署
认证方案OIDC / SAML支持 SSO,降低密码泄露风险依赖 IdP 可用性企业级多租户
数据隔离模式独立数据库最强安全隔离,备份恢复独立运维成本高,跨租户统计困难金融、医疗
数据隔离模式共享表 + RLS运维简单,按需伸缩需确保每个查询都带租户过滤,易出错中小规模 SaaS

3.6 风险点

  1. 租户 ID 伪造:如果租户 ID 仅依赖请求中的 Header 或路径,而不在 token 中签名,恶意用户可构造请求访问其他租户数据。风险在租户识别层与认证层之间。
  2. 认证层与授权层信任链断裂:如果授权层直接接收 HTTP 请求属性而不校验 token 签名,攻击者可注入虚假身份。
  3. 数据隔离层遗漏:开发人员编写的 SQL 忘记带 tenant_id 条件,或 ORM 框架未强制注入过滤策略,导致数据泄露。
  4. 审计日志绕过:若审计层仅在授权允许后记录,则授权拒绝的恶意尝试将不可追溯,难以检测攻击前奏。
  5. 跨层状态不一致:例如租户在认证层被停用,但授权层仍缓存旧策略,导致已被停用的用户仍能操作。
【风险案例】2021 年某知名协作平台因共享表模式中 ORM 未强制注入 tenant_id,导致用户通过修改 API 参数访问其他企业的文档,影响数百万客户。事后该平台切换至独立 Schema + 查询拦截器方案。

3.7 落地步骤

  1. 定义租户识别策略:确定使用域名、路径还是 Header,并在 API 网关或中间件中统一提取 tenant_id。
  2. 实现认证层时绑定租户:用户注册时要求选择租户(或自动匹配),登录后 token 中必须包含 signed_tenant_id。
  3. 设计租户级 RBAC 存储:每个租户的角色表、权限表、用户-角色分配表都包含 tenant_id 字段,并建立复合主键 (tenant_id, id)。
  4. 集成授权决策点:选择 Policy Engine(如 OPA)或自建服务,将操作、资源、用户角色作为输入,输出 permit/deny。
  5. 实现数据隔离层:根据选型(共享表 / 独立 Schema / 独立数据库)配置 ORM 拦截器或数据库行级安全策略(RLS)。
  6. 搭建审计管道:定义审计事件结构,在关键业务代码中埋点异步发送至审计服务。保证审计日志独立于业务数据库。
  7. 端到端集成测试:构造跨租户访问测试用例,验证每层是否能正确拦截。

3.8 验收标准

验收项预期结果验证方式
租户 A 的用户无法访问租户 B 的数据返回 403 或空结果自动测试:模拟用户 A 请求租户 B 的资源
租户管理员只能管理本租户的角色修改接口只影响本租户手动测试 + 审计日志检查
认证失败请求不穿透到授权层在认证层直接返回 401mock 认证层场景测试
审计日志可查询指定租户的最近 100 条记录返回正确过滤的结果API 调用验证
租户停用后,该租户的所有 token 立即失效下一请求被拒绝模拟停用后验证 token 是否被黑名单拦截
【落地检查表】交付前应逐层检查:① 租户识别层是否在网关/中间件完成?② 认证层 token 是否包含 tenant_id 且签名?③ 授权层策略存储是否按租户分区?④ 数据隔离层是否在 ORM 层面自动注入 tenant_id?⑤ 审计日志是否包含租户字段且不可删除?