API 网关专栏 · 第 89 章

多租户 SaaS API 网关:隔离、配额与租户感知策略

2026年09月22日
多租户 SaaS API 网关:隔离、配额与租户感知策略

多租户 API 网关应把经过验证的租户上下文编译为路由访问、配额和可观测性策略。它不能因为主机名、路径片段、Token 声明或 X-Tenant-ID 请求头中出现了某个值,就直接信任该值。

网关负责保护共享入口,应用和数据层仍然负责对象级授权与持久的数据隔离。本文聚焦这一策略边界;更广泛的单元化与区域拓扑,请参阅面向大规模 SaaS 平台的 API 网关架构。

核心要点

  • 先把经过认证的主体映射到权威租户,再应用租户策略。
  • 向上游转发内部租户值前,应删除、拒绝或覆盖调用方提供的租户上下文。
  • 分开处理路由资格、流量配额、并发量、成本与数据授权。
  • 把 Consumer Group 视为可复用的网关策略,而不是数据库隔离的证明。
  • 针对每类租户测试默认、故障、绕过、例外和凭据轮换路径。
  • 限定策略变更范围,避免单一租户的例外悄然削弱所有租户的保护。

定义租户策略契约

租户感知策略不应只有一个租户 ID:

字段负责人决策示例
主体身份系统与网关认证哪个凭据或 Token 已通过验证?
租户成员关系权威租户目录该主体可以代表哪些租户?
路由权益产品或策略负责人该租户可以调用哪个 API 和操作?
运行时预算平台与服务负责人适用怎样的请求、并发、负载或成本限制?
资源授权应用当前主体是否可以操作该订单或账户?
数据隔离服务与存储层可以使用哪些行、Schema、数据库、区域或密钥?

不要用一个字段承载以上六项决策。有效的租户成员关系不等于可以访问所有对象,配额标签也不是授权结果。

建立从身份到策略的证据链

1sequenceDiagram
2    participant C as 客户端
3    participant G as API 网关
4    participant I as 身份验证方
5    participant D as 租户目录与策略源
6    participant A as 租户感知应用
7    participant S as 隔离的数据边界
8    C->>G: 凭据与可选租户提示
9    G->>I: 按契约验证凭据
10    I-->>G: 已验证的主体上下文
11    G->>D: 解析允许的租户与策略版本
12    D-->>G: 租户、路由、配额与单元
13    alt 租户提示与已授权成员关系冲突
14        G-->>C: 拒绝请求
15    else 租户上下文已获授权或未提供提示
16        G->>G: 移除或替换公开租户提示
17        G->>A: 通过受保护链路传递最小可信上下文
18        A->>A: 执行对象与工作流授权
19        A->>S: 执行租户范围内的数据操作
20        S-->>A: 返回租户范围内的结果
21        A-->>G: 响应
22        G-->>C: 响应
23    end

身份验证方负责凭据有效性,租户目录负责成员关系和位置,网关执行编译后的边界策略,应用与存储层负责领域和数据访问。每条箭头都应有明确的故障策略与责任人。

如果客户端需要在多个获准租户中选择,应把它提交的值视为“请求使用的上下文”。先将它与权威成员关系比对,再生成唯一的内部值。只有通过调用方无法绕过或冒充的认证链路,上游才能信任这个内部值。

分层实施隔离

路由与操作隔离

只允许租户访问已购买或获分配的 API 产品、版本和方法。粗粒度路由白名单应在上游工作开始前失败,应用仍需验证被请求的具体资源。

容量隔离

分别设置请求数、并发工作量、长连接、大请求体和高成本操作预算。一次请求可能触发多次数据库查询、连接器调用或模型 Token 消耗,仅按请求数无法公平衡量成本。

故障域隔离

需要明确:一个高噪声或配置错误的租户是否会影响共享网关、共享计数存储、共享上游池或整个区域。只有在风险与运维成本确实支持时,才采用单元或专属容量。

可观测性隔离

租户标签有助于事故响应,但也可能泄露客户名称或造成高指标基数。优先使用稳定的内部标识符、访问控制和保留期限,并通过另一份受权限保护的映射关联客户名称。

有意识地使用 APISIX Consumer Group

Apache APISIX 3.19.0 的 Consumer Group 可把通用插件配置附加给 Consumer。consumer-restriction 插件可在认证插件选出 Consumer 后,按 Consumer、Route、Service 或 Consumer Group ID 进行允许或拒绝。

下面是 Admin API 请求体片段。它们假设经过认证的 Consumer 已由服务端绑定到 tenant-acme;调用方无法通过数据面请求设置 group_id。

1{
2  "plugins": {
3    "limit-count": {
4      "count": 1200,
5      "time_window": 60,
6      "policy": "redis",
7      "redis_host": "redis.internal",
8      "redis_port": 6379,
9      "redis_ssl": true,
10      "redis_ssl_verify": true,
11      "key_type": "constant",
12      "key": "tenant-acme",
13      "group": "tenant-acme-orders",
14      "allow_degradation": false,
15      "show_limit_quota_header": true,
16      "rejected_code": 429
17    }
18  }
19}
1{
2  "plugins": {
3    "consumer-restriction": {
4      "type": "consumer_group_id",
5      "whitelist": ["tenant-acme"],
6      "rejected_code": 403
7    }
8  }
9}

第一个请求体属于 Consumer Group,第二个属于受保护的 Route 或 Service。key_type: constant 让已配置的租户组共用一个计数键。另一个 group 值用于让匹配的 limit-count 配置跨路由共享计数器;它不是租户身份。示例有意省略 Redis 认证与信任存储部署细节,生产环境必须按实际部署配置。

limit-count 文档明确了若干重要默认值和分支:

  • policy 默认为 local,此时每个 APISIX 实例都有自己的计数器。Redis 策略可在多个实例间协调计数器,同时引入新的依赖。
  • key_type 默认为 var,key 默认为 remote_addr;同时省略两者会产生源 IP 计数器,而不是租户级共享配额。
  • 对单节点 redis 策略,redis_ssl 默认为 false。false 路径使用明文 Redis 连接,true 启用 TLS。redis_ssl_verify 也默认为 false:启用 TLS 后,false 只加密连接而不验证 Redis 服务端证书,true 则依据已配置的信任根与服务端名称验证证书。这些传输设置都不会验证调用方或其租户身份;Redis 凭据和证书信任边界必须单独管理。
  • allow_degradation 默认为 false。设为 false 时,计数后端故障或动态 count/window 值无效会返回 HTTP 500;设为 true 时,请求会跳过本限流器继续处理。两条路径都不会改变租户身份的建立方式。
  • show_limit_quota_header 默认为 true。设为 true 时响应包含配额请求头,设为 false 时不发送。请求头只是暴露给调用方的执行状态,上游绝不能把它们当作可信身份或计费证据。

Consumer 插件优先级同样重要。APISIX 文档指出,如果某个插件直接配置在 Consumer 上,它会覆盖从 Consumer Group 继承的同名插件。治理流程必须防止未经审查的单 Consumer 覆盖长期变成租户例外。

编译策略,而不是每次请求都查询

租户套餐、成员关系、例外和生命周期应保存在权威系统中,再把网关需要的子集编译成带负责人、开始生效时间和例外到期时间的版本化配置。

如果使用动态查询,需要明确:

  • 缓存时长与撤销暴露窗口;
  • 目录缺失、缓慢、过期或相互矛盾时的行为;
  • 现有会话能否继续;
  • 租户在单元之间迁移时如何协调;
  • 网关如何证明本次请求使用了哪个策略版本。

故障开放和故障关闭不是适用于所有场景的价值判断。可选分析标签可以故障开放,而租户认证、跨租户路由或高成本操作预算可能需要故障关闭,或切换到更小的应急额度。

测试跨租户负向路径

正向测试只能证明一个允许的请求成功了。测试矩阵还应包含:

  • 租户 A 的凭据搭配租户 B 的请求头、主机名、路径、对象 ID 和单元提示;
  • 有效凭据但没有任何租户成员关系;
  • 已过期、已撤销、Issuer 错误或 Audience 错误的凭据;
  • Consumer 已迁移到其他组,但旧凭据仍有效;
  • 绕过网关直接请求上游;
  • 在 allow_degradation: false,以及另一项经过批准的 true 策略下丢失计数存储;
  • 同一租户从多个源 IP 消耗共享配额;
  • 与组策略冲突的单 Consumer 插件覆盖;
  • 可能泄露其他租户标识或流量的日志与指标。

租户策略检查清单

  • 租户上下文是否来自已验证身份和权威成员关系?
  • 调用方控制的任何值能否覆盖内部租户映射?
  • 路由策略是否区分产品访问与对象授权?
  • 是否在需要时分别控制请求、并发、负载和高成本操作?
  • 本地与分布式计数器语义是否符合规则要求?
  • allow_degradation 的两条路径是否都有文档和测试?
  • 继承插件与单 Consumer 插件的优先级是否经过审查?
  • 租户例外能否自动到期并追溯到负责人?
  • 是否可以绕过已认证的网关链路直接访问上游?
  • 能否在不暴露租户机密信息或个人数据的情况下还原事故?

常见问题

Consumer Group 是否等同于 SaaS 租户?

不等同。运维人员正确绑定 Consumer 时,它可以代表租户策略组,但无法证明成员关系、对象授权或存储隔离。

每个限流键都应包含租户 ID 吗?

只有当租户值来自受控上下文,而且符合目标预算时才应这样做。组专属常量通常比公开请求头更安全;按用户或操作限流时还可能需要其他维度。

网关隔离能否替代行级安全?

不能。网关无法观察所有内部数据访问,也无法证明服务正确限定了查询范围。应用和数据层必须再次执行租户边界。

后续步骤

把租户策略连接到大规模 SaaS 单元与故障域、分布式配额语义和细粒度应用授权。

获取方案