细粒度 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 网关安全扫描体系。
