4. 授权策略设计与集中管理

授权策略是权限系统的“法律条文”:它规定谁在什么条件下能对哪些资源执行什么操作。策略设计一旦失控,常见后果是权限蔓延、审计困难、变更风险高。本节围绕策略语言、策略存储、PDP/PEP 分离、集中式与分布式架构对比,以及策略版本管理与灰度发布展开,目标是让策略从“散落在代码里的 if-else”变成可管理、可审计、可回滚的工程资产。

策略语言:从自然语言到可判定表达式

策略语言的核心任务是把授权规则表达成机器可判定的形式。常见选择有三类:声明式策略语言(如 Rego、Cedar)、基于属性的表达式(如 ABAC 中的条件表达式)、以及关系模型(如 ReBAC 的关系图查询)。选择时重点看三点:表达力是否覆盖业务场景、是否可静态分析、是否有成熟的决策引擎与测试工具。

以 Rego 为例,一条“财务专员仅在工作日 9:00–18:00 可审批金额不超过 5 万元的报销单”的策略可以写成:

allow if {
  input.user.role == "finance_specialist"
  input.action == "approve"
  input.resource.type == "expense_report"
  input.resource.amount <= 50000
  time.weekday(time.now_ns()) >= 1
  time.weekday(time.now_ns()) <= 5
  time.hour(time.now_ns()) >= 9
  time.hour(time.now_ns()) < 18
}

这段策略的输入是 input(用户、动作、资源属性),输出是布尔决策。注意 amount <= 50000 是数值比较,time.weekday 返回 1–7,周一为 1。策略语言必须能明确表达“默认拒绝”:只有 allow 为真才放行,否则拒绝。这比“默认允许再逐条排除”安全得多。

策略存储:集中式仓库与版本化

策略不能只存在于某个服务的配置文件里。推荐做法是把策略集中存放在策略仓库中,通常是一个 Git 仓库或专用的策略存储服务。Git 仓库的好处是天然具备版本、分支、评审和回滚能力;专用存储服务则更适合高频更新和运行时查询。

策略存储需要解决三个问题:唯一事实源(所有 PDP 从同一处加载策略)、版本标识(每次发布有明确的版本号或 commit hash)、加载一致性(多个 PDP 实例加载到同一版本)。实践中常用“策略包 + 版本号”的方式:策略包是一个目录,包含若干 .rego 文件和测试文件,发布时打上语义化版本,例如 authz-policy@2.3.1。

PDP 与 PEP 分离:决策与执行的解耦

PDP(Policy Decision Point,策略决策点)负责根据策略和输入计算出“允许/拒绝”,PEP(Policy Enforcement Point,策略执行点)负责在真实请求路径上拦截并执行决策。两者分离的价值在于:策略变更不需要修改业务代码,PEP 只关心“问 PDP 要一个决策”,PDP 只关心“根据策略算一个决策”。

典型调用链是:客户端请求 → PEP 拦截 → 组装 input(用户、动作、资源、环境)→ 调用 PDP → PDP 返回 allow/deny 及原因 → PEP 放行或拒绝。PEP 通常以 SDK、Sidecar 或网关插件形式存在。PDP 可以是本地库(低延迟)或远程服务(集中管理)。

PDP / PEP 分离与策略版本加载 客户端 请求资源 PEP 执行点 拦截 / 组装 input 放行或拒绝 PDP 决策点 加载策略版本 计算 allow / deny 策略仓库 Git / 存储服务 版本 authz-policy@2.3.1 返回决策 + 原因 拉取策略包 / 热加载 关键约束: 1. 所有 PDP 实例必须加载同一策略版本,避免决策不一致; 2. PEP 不缓存决策结果,或缓存必须带版本号与短 TTL;3. 策略变更走灰度发布,先小流量验证再全量。

集中式与分布式授权架构对比

集中式架构下,所有 PDP 调用同一个远程决策服务,策略只存一份,变更立即生效,审计日志集中。缺点是决策服务成为关键路径,网络延迟和可用性风险集中。分布式架构下,每个服务或节点本地运行 PDP,策略包定期同步,决策延迟低,但版本一致性难保证,策略更新有传播延迟。

维度集中式分布式
决策延迟含网络往返,通常 5–20ms本地计算,通常 <1ms
策略一致性强,单一事实源最终一致,存在传播窗口
可用性风险决策服务故障影响面大单节点故障影响面小
变更生效速度立即取决于同步周期,秒级到分钟级
适用场景策略频繁变更、强审计要求高吞吐、低延迟、策略稳定

实际生产常采用混合模式:核心交易链路用本地 PDP 保证低延迟,管理后台和低频操作走集中式 PDP 保证一致性。无论哪种模式,策略版本号都必须随决策结果一起记录,否则故障排查时无法判断“当时用的是哪版策略”。

策略版本管理与灰度发布

策略变更的风险不亚于代码变更。推荐流程是:策略即代码——策略文件进 Git,走 Pull Request 评审,CI 中运行策略单元测试,合并后打版本标签。发布时先灰度:将新版本策略加载到一小部分 PDP 实例,观察决策差异和拒绝率变化,确认无误后再全量。

灰度发布的关键是可对比。可以在 PDP 中同时加载新旧两版策略,对同一请求分别计算,记录差异日志(shadow mode)。例如新策略将某接口的拒绝率从 0.3% 提升到 2.1%,就需要人工确认这 1.8% 的增量是预期收紧还是误伤。确认后再切换流量。

实践建议:策略仓库中每个策略包应包含 policy.rego、policy_test.rego 和 CHANGELOG.md。测试文件覆盖允许、拒绝、边界条件三类用例。版本号遵循语义化版本:主版本表示不兼容的决策语义变更,次版本表示新增规则,修订版本表示修复。
常见陷阱:不要用“默认允许 + 黑名单”模式。一旦黑名单遗漏,未列出的操作会被放行。正确做法是默认拒绝,只显式允许。另外,策略中避免硬编码用户 ID 或资源 ID,应使用角色、属性或关系查询,否则策略会随业务数据膨胀而不可维护。

总结来看,授权策略设计的核心是把“谁能做什么”从散落的代码逻辑中抽离为可版本化、可测试、可灰度的策略资产。PDP/PEP 分离让决策与执行各司其职,集中式与分布式架构的选择取决于延迟、一致性和可用性的权衡,而版本管理与灰度发布则是策略变更安全落地的最后一道防线。