API 网关专栏 · 第 59 章

细粒度 API 网关授权:RBAC、ABAC 与外部策略决策

2026年09月10日
细粒度 API 网关授权:RBAC、ABAC 与外部策略决策

细粒度 API 授权判断的是:经过验证的主体在当前条件下,能否对特定资源执行具体操作。API 网关适合在流量到达服务前执行路由、方法、客户端、Scope 和粗粒度租户策略;但它通常不适合判断某位用户是否拥有对象 123、某张发票是否仍可编辑,或某项工作流转换是否合法。这些事实属于应用,或属于能够访问权威数据的策略服务。

应把授权设计成一条明确的决策链:先认证,再规范化可信上下文,评估策略,执行结果,同时保留应用级检查。身份提供商返回 200,或 Token 中存在角色名称,都不能单独证明调用方拥有所有操作权限。

核心要点

  • 区分身份认证、授权决策、策略执行和业务状态验证。
  • 使用 RBAC 表达稳定的岗位权限,使用属性约束租户、资源、操作和环境。
  • 只向外部策略决策点发送可信且必要的输入。
  • 对受保护操作保持故障关闭,除非已有文档化风险决策允许降级。
  • 像管理应用代码一样,对策略进行版本管理、测试、观测和回滚。

划分授权责任

1flowchart LR
2    C[客户端] --> G[网关策略执行点]
3    I[身份提供商] -->|已验证声明| G
4    G -->|决策输入| P[策略决策点]
5    P -->|允许或拒绝及原因| G
6    G --> A[应用]
7    A -->|对象与工作流检查| D[(领域数据)]
层级适合在本层完成的决策通常需要其他层完成的决策
身份提供商谁已认证、认证保障级别、分组、Token Audience当前是否可以访问某个 API 对象
API 网关路由、方法、客户端、Scope、粗粒度租户、网络条件对象归属与当前业务状态
策略服务使用获准属性执行跨服务策略它无法从权威来源获取的事实
应用或领域服务对象关系、工作流、记录状态、字段级规则集群级边缘准入和流量控制

网关不能把公网客户端提供的 X-Role、X-Tenant 或类似请求头用作策略输入,除非可信组件先删除客户端值,再创建经过认证的替代字段。

选择 RBAC、ABAC 或组合使用

基于角色的访问控制(RBAC,Role-Based Access Control)把权限分配给角色,再把角色分配给主体。它适合 support-reader 或 billing-admin 等稳定岗位职能,前提是角色范围清晰并定期复核。

基于属性的访问控制(ABAC,Attribute-Based Access Control)根据主体、资源、操作以及有时还包括环境的属性来评估策略。NIST SP 800-162定义了这一模型及其实施考量。ABAC 可以表达:

  • 主体租户必须与资源租户一致;
  • Token 必须包含所需 Scope 和 Audience;
  • 写操作只能来自获准的工作负载和环境;
  • 支持人员角色可以查看敏感记录,但不能导出;
  • 紧急权限只在到期时间之前有效。

RBAC 与 ABAC 并不互斥。策略可以先匹配角色,再通过租户、操作、资源分类、认证保障级别或时间加以约束。不要为每种上下文组合创建角色,否则会导致角色爆炸,并掩盖真正的决策输入。

定义决策契约

设计外部策略契约时,应围绕经过规范化的可信输入展开:

  • 主体 ID、类型、颁发者、租户、认证保障级别和获准声明;
  • 路由或服务 ID、HTTP 方法、规范化操作和环境;
  • 在能够安全获取时提供资源类型和标识;
  • 来自权威来源的相关资源属性;
  • 策略 Bundle 或版本,以及请求关联 ID。

响应应精简且确定:允许或拒绝、有限集合的原因码、策略版本、可选义务和缓存指引。不要向客户端返回密钥或内部策略细节。把决策服务返回的请求头视为特权数据,只允许白名单内字段进入上游。还要验证集成实际发送的内容:如果传输的上下文超出契约需要,决策服务就进入了同一敏感认证边界。

将 APISIX 与 OPA 集成

Apache APISIX 3.18 的 opa 插件可以把请求上下文发送给 Open Policy Agent。配置的 policy 路径必须返回含 allow 字段的结果对象;可选字段包括 reason、headers 和 status_code。

在 APISIX 3.18 中,OPA 输入包含请求头,但受运行时请求头枚举上限约束;策略不得假设该请求头集合完整。启用 with_route、with_service 或 with_consumer 时,还会加入对应的 APISIX 对象;发送前,APISIX 会从路由和服务对象副本中移除 upstream。其余请求头和对象字段仍可能包含凭证或敏感配置。应把 OPA 视为认证边界内的特权服务,保护并限制其连接;除非已经审查准确载荷,否则保持可选对象关闭。

以下示例路由调用内部 OPA 决策。它假设更早的可信认证阶段已经产生策略能够理解的上下文;该阶段不在本片段范围内:

1{
2  "uri": "/v1/reports/*",
3  "methods": ["GET", "POST"],
4  "plugins": {
5    "opa": {
6      "host": "https://opa.internal:8181",
7      "policy": "api/reports/authz",
8      "ssl_verify": true,
9      "timeout": 1000,
10      "with_route": false,
11      "with_service": false,
12      "with_consumer": false
13    }
14  },
15  "upstream": {
16    "type": "roundrobin",
17    "nodes": {"reports.internal:8080": 1}
18  }
19}

请根据实际部署的 APISIX 版本验证所有字段。盘点到达 OPA 的请求头,在进入该边界前尽可能删除不必要的客户端可控请求头,并防止请求体或决策响应体进入通用日志。保护 APISIX 到 OPA 的连接,在支持时验证双方身份,限制网络访问,并监控决策延迟和错误。

APISIX 还提供 forward-auth,可连接外部认证或授权服务。其 request_headers 设置用于选择要复制的客户端请求头;同时,APISIX 会在授权请求中另行添加 X-Forwarded-Proto、X-Forwarded-Method、X-Forwarded-Host、X-Forwarded-Uri 和 X-Forwarded-For。当 request_method 为 POST 时,APISIX 还会复制客户端的 Content-Encoding 请求头并发送缓冲后的请求体;配置的 extra_headers 也可以增加其他字段,包括由 APISIX 变量解析出的值。该插件还支持响应头允许列表、超时、status_on_error 和 allow_degradation。若要保持最小授权请求契约,应使用默认的 GET 方法,仅在必要时配置 extra_headers,并通过 request_headers 明确设置允许列表。请选择一种集成模型,不要串联多个优先级不清晰的不透明策略调用。

决定故障与缓存行为

对于受保护的写操作,策略服务不可用时通常应拒绝请求或返回可区分的服务错误,而不是静默授权。APISIX forward-auth 文档指出 allow_degradation: false 是默认值。如果低风险读取允许降级,应记录威胁模型、最长持续时间、陈旧策略边界、负责人、告警和恢复操作。

授权缓存可以降低延迟,但缓存键必须包含所有与策略相关的维度:主体、操作、资源、租户、策略版本和可变上下文。TTL 应符合吊销和角色变更要求。绝不能把一位用户的允许决策用于另一位用户或更广泛的资源。

不要使用会在策略服务故障时放大流量的重试。应采用有限超时、容量隔离、健康信号,以及经过验证的响应路径。

在应用中保留对象授权

网关可以提取 /accounts/123,却未必知道账号 123 是否属于调用方、是否已经转移或关闭,以及是否包含受限字段。读取权威状态的服务必须在每次操作时执行这些关系检查。

把网关拒绝作为早期过滤器,而不是应用可以跳过检查的证明。对于集合、导出、GraphQL 字段和批量操作,应针对实际返回的对象和字段进行授权,不能只授权路由。下游服务还要知道哪个主体及委托上下文发起请求,避免代理人混淆问题。

像管理代码一样治理策略

  • 在版本控制中保存策略、Schema 和测试用例。
  • 要求资源负责人以及安全或平台负责人共同审查。
  • 同时测试允许和拒绝场景,而不只是预期成功路径。
  • 使用合成租户和对象覆盖跨租户访问。
  • 逐步发布策略版本,并保留上一已知良好版本以便回滚。
  • 记录决策原因和策略版本,同时避免暴露敏感内部细节。
  • 定期复核角色、属性、紧急访问和未使用权限。
  • 自动终止例外和临时授权。

即使没有更改应用二进制文件,授权变更仍然是生产行为变更。

测试决策矩阵

每项敏感操作都应覆盖:

场景预期结果
缺少凭证或凭证无效身份认证失败
主体有效但缺少权限授权拒绝
角色正确但租户错误拒绝
租户正确但操作不允许拒绝
路由允许但访问他人对象应用拒绝
授权过期或已吊销在文档规定的传播窗口内拒绝
策略服务超时或响应无效既定故障关闭或获准降级行为
策略版本回滚恢复并可观察到上一已知良好行为

还应测试请求头伪造、大小写和路径规范化、方法覆盖、批量请求、缓存隔离、陈旧声明和策略服务过载。确认日志能够区分认证失败、策略拒绝、应用拒绝和基础设施错误。

授权设计检查清单

  • 哪个组件认证主体,哪些声明是可信的?
  • 每条路由代表什么操作和资源?
  • RBAC 角色是否稳定且范围清晰,并使用属性表达上下文约束?
  • 哪些决策需要权威应用状态?
  • 策略输入是否最小化、规范化,并受到请求头伪造防护?
  • 超时、响应无效或策略服务中断时会怎样?
  • 所有缓存决策是否按相关维度和策略版本隔离?
  • 是否能在不泄露密钥的情况下解释并关联每个允许或拒绝决策?
  • 策略变更是否经过审查、测试、灰度发布并可回滚?

总结

各层职责清晰时,细粒度网关授权才能可靠运行。网关提前拒绝明显未授权的路由和方法,外部策略服务可以评估共享的 RBAC 与 ABAC 规则,而应用继续执行需要领域状态的对象及工作流检查。可信输入、故障关闭、边界明确的缓存、策略测试和决策证据,能让集中授权成为可靠控制,而不是新的单点模糊地带。

FAQ

如果 Token 包含角色,身份认证是否已经足够?

不够。身份认证只验证身份和 Token 属性。授权仍需把可信角色和其他属性映射到当前请求的操作与资源。

是否应该把所有授权都迁移到 API 网关?

不应该。可以集中处理网关能够准确评估的决策,但对象归属、记录状态和字段级规则仍应留在应用或具有权威数据的策略服务中。

策略服务不可用时,授权应该故障开放吗?

受保护操作通常应保持故障关闭。任何降级读取路径都需要明确的风险决策、有限持续时间、陈旧策略规则、监控和恢复方案。

后续步骤

先通过 API 网关 mTLS建立工作负载身份,再查看 OAuth、JWT 与 OIDC 身份认证,并把授权用例加入 API 网关安全扫描体系。

获取方案