Skip to content

第 5 章:给用户一个“管理员角色”,真的能解决授权吗? ​

本章主线:从角色爆炸和跨租户越权出发,逐步建立 RBAC、ABAC、ReBAC、策略架构、测试和灰度发布能力。

开场问题 ​

小安为项目系统设计权限:普通用户、项目管理员、超级管理员。很快业务又提出地区管理员、只读管理员、临时管理员和外包管理员。角色越加越多,仍回答不了“为什么 Alice 能读这份文档”。

问题不只是角色数量,而是资源关系、动态上下文、例外生命周期和解释能力没有进入模型。授权设计先定义决策输入,再选择模型。

学习目标 ​

能从业务对象定义 subject/resource/action;选择 RBAC、ABAC 或 ReBAC;设计 PAP/PDP/PEP/PIP;实现策略测试、影子发布、缓存撤销、解释和审计。

5.1 授权的六元组 ​

text
(subject, resource, action, context, delegation, policy-version) → decision

决策结果可以是 allow、deny、not-applicable,也可以附带 obligations:脱敏字段、限制行数、要求 MFA、记录理由、二人审批。

5.1.1 资源与动作命名 ​

资源使用稳定、层次清晰的类型与 ID:tenant/t1/project/p9/document/d7。动作使用业务语义:document.read、invoice.approve,而非只用 HTTP GET/POST。相同 POST 可能是预览或付款,风险完全不同。

上下文只放决策当时可信且必要的信息。用户可控制的请求头不能直接成为 device_compliant=true。

5.2 ACL 与 RBAC ​

ACL 直接列出资源允许谁访问,直观但全局治理困难。RBAC 将权限赋给角色、用户获得角色,适合职责稳定的企业。

RBAC 失败模式:每个部门/地区/项目组合创建角色,形成角色爆炸;临时例外永久保留;角色名称不能解释真实权限;嵌套角色形成隐蔽继承。

治理方法:角色 owner、用途、成员规则、权限清单、有效期、定期复核、使用率与删除机制。

5.2.1 角色工程 ​

先做 job/function role,避免按个人建角色。将通用低风险权限设为出生角色,高风险权限拆出申请型角色;临时项目权限必须有 expiry。统计角色成员分布、权限使用率和与同岗位偏差,识别角色污染。

5.3 ABAC ​

依据主体、资源、动作和环境属性判断。例如:department == resource.ownerDept AND device.compliant AND data.level <= subject.clearance。

ABAC 的难点是属性治理:来源是否权威、多久更新、缺失如何处理、语义是否一致、能否被用户修改。默认对缺失安全属性拒绝,避免把 null 当低风险。

5.3.1 示例策略 ​

text
permit if
  subject.tenant == resource.tenant
  and subject.department == resource.owner_department
  and subject.clearance >= resource.classification
  and context.device_compliant == true
  and action == "document.read"

逐项测试 tenant 缺失、部门刚调动、设备信号过期、classification 未知和大小写/枚举不一致。策略正确性依赖属性契约。

5.4 ReBAC ​

用关系图表达 user:alice member team:payroll、team:payroll editor doc:q3。适合协作、分享和层次资源。

图 5-1:5.4 ReBAC

图 5-1:5.4 ReBAC · 可编辑 Mermaid 源码

设计需定义关系 schema、继承、环路、删除传播、图深度、缓存和一致性。回答“为什么允许”时返回关系路径,但要防止解释泄漏不可见对象。

5.4.1 Schema 思考 ​

text
type document:
  relation parent: folder
  relation owner: user
  relation editor: user | group#member
  permission read = owner or editor or parent->reader

Schema 变更属于数据迁移:旧关系是否仍有效、回滚如何解释、循环继承如何阻止、批量 list objects 如何高效执行,都需要测试。

5.5 策略架构:PAP/PDP/PEP/PIP ​

图 5-2:5.5 策略架构:PAP/PDP/PEP/PIP

图 5-2:5.5 策略架构:PAP/PDP/PEP/PIP · 可编辑 Mermaid 源码

  • PAP 管理策略生命周期;PDP 纯决策;PEP 必须在无法绕过的位置执行;PIP 提供可信属性。
  • 把检查散落在 UI 中不算授权,攻击者可直接调用 API。
  • 服务内部对象级授权仍需执行,网关只能覆盖粗粒度边界。

5.5.1 Check API 示例 ​

json
{
  "subject": {"type": "user", "id": "u123", "tenant": "t1"},
  "action": "invoice.approve",
  "resource": {"type": "invoice", "id": "i789", "tenant": "t1"},
  "context": {"device_compliant": true, "auth_age_seconds": 120}
}

响应:

json
{
  "decision": "step_up",
  "reason_codes": ["RECENT_PHISHING_RESISTANT_AUTH_REQUIRED"],
  "policy_version": "payroll@8f37",
  "obligations": [{"type": "max_auth_age", "seconds": 300}]
}

PEP 必须理解并执行 obligations;无法识别的安全 obligation 默认拒绝,不能静默忽略。

5.6 策略即代码 ​

需要版本控制、代码评审、静态检查、单元/属性测试、仿真、灰度、决策差异、回滚。策略发布与应用发布解耦,但不能无审批直达生产。

测试包括:明确 allow、明确 deny、属性缺失、跨租户、边界时间、继承、冲突策略、旧策略回放。安全不变量可写为“任何非本租户主体永不允许读取本租户资源”。

5.6.1 发布流水线 ​

text
语法/类型检查 → 单元测试 → 安全不变量 → 历史流量回放
→ 新旧决策差异评审 → 影子模式 → 1% PEP 灰度 → 全量

差异报告按 allow→deny、deny→allow 分类,后者通常风险更高。紧急 deny 可走快速通道,但仍记录审批、过期时间和事后评审。

5.7 冲突与默认策略 ​

通常默认拒绝,显式允许;高风险 deny 可优先于 allow。需要写清组合算法,避免不同服务各自理解。Break-glass 是受控例外:强认证、短时、限定资源、理由、实时告警、事后复核。

5.8 缓存、一致性与性能 ​

授权延迟直接影响业务。可缓存属性、关系或决策,但 key 必须包含租户、主体/资源版本和策略版本。撤权事件主动失效,TTL 限制最坏暴露窗口。

压测不能只测平均延迟:测试热点资源、深关系、批量列表、缓存冷启动、策略发布和 PIP 故障。列表接口要避免逐项 PDP 导致 N+1,可使用过滤条件/批量检查。

5.8.1 撤权暴露窗口 ​

最坏暴露约等于:事件产生延迟 + 传播延迟 + 缓存 TTL + 客户端重试/会话延迟。只把缓存设为 5 分钟,不代表撤权 5 分钟内完成。对高危权限用主动失效、短 TTL 和资源端版本检查组合。

5.9 决策解释与审计 ​

记录:主体、资源、动作、结果、理由码、命中规则、策略版本、关键属性版本、委托链、request ID。不要把完整敏感属性全写日志。

对用户给可理解但不泄密的解释;对调查员给受控的详细证据;对开发者给 trace。三者不是同一输出。

5.9.1 隐私与性能 ​

审计不应复制整个 JWT 或属性对象。记录稳定 ID、属性版本/哈希和命中规则即可。高吞吐场景用异步可靠管道,但高风险写操作若审计完全不可用,要有明确降级决策。

5.10 选型方法 ​

  • 职位职责稳定:RBAC 打底。
  • 风险和资源属性重要:叠加 ABAC。
  • 协作分享和层次资源:ReBAC。
  • 实际系统通常组合,但先确定主模型,避免“万能策略语言”掩盖混乱。

5.10.1 对比矩阵 ​

维度RBACABACReBAC
核心输入角色成员与权限主体/资源/环境属性实体间关系路径
擅长稳定岗位职责动态上下文和数据分级协作、分享、层次资源
主要风险角色爆炸属性不可信/策略复杂图一致性和性能
解释命中哪个角色命中哪些条件命中哪条关系路径
治理重点角色 owner/复核属性契约/来源schema/关系生命周期

5.11 本章实验与验收 ​

实验:做文档系统 ReBAC;加入设备合规 ABAC;实现策略版本、批量 check、解释和影子发布。生成 10 万关系并测试 P99。

验收:能回答“谁能访问资源”“用户能访问什么”“为何允许”;证明跨租户不变量;演示撤权在约定 SLA 内生效;回滚错误策略。

5.11.1 评测数据集 ​

构造至少 50 个固定案例:同租户正常、跨租户、禁用主体、过期关系、组嵌套、循环、属性缺失、策略冲突、break-glass、缓存旧版本。每个案例保存预期 decision 与 reason code,作为策略回归门禁。

工程复盘:角色没配错,为什么仍然发生跨租户读取 ​

第一版授权规则写着“项目管理员可以读取项目文档”。小安认为角色判断没有问题,测试也覆盖了管理员与普通用户。评审同事只追问一句:项目是谁的项目?这才暴露出规则没有把租户和资源关系放进决策。

团队把授权测试从若干示例升级为安全不变量:不同租户的主体永远不能读取对方资源;被移出项目后不得继续命中缓存;策略服务超时时,高风险动作不能因旧 allow 结果继续执行。一个最小拒绝样例如下:

text
subject.tenant = t1
resource.tenant = t2
action = document.read
expected = deny(cross_tenant)

小安随后增加策略差异检查、历史请求回放、影子决策和 deny→allow 高风险审批。发布后不只观察策略错误率,还观察决策延迟、缓存年龄、拒绝原因分布和权限扩大范围。

授权平台的价值不是让策略“能写”,而是让一次权限扩大能够被解释、测试、灰度、回滚并留下证据。

面试与设计评审 ​

  • RBAC、ABAC、ReBAC 怎么选?先说业务关系,再谈模型优缺点,避免“组合全部”。
  • PEP 为什么必须不可绕过?用 UI 隐藏按钮但 API 可直调解释。
  • 策略发布如何避免全站拒绝?回答测试、历史回放、影子差异、灰度、指标和快速回滚。

本章小结 ​

授权是可测试、可解释、可演进的决策系统。角色只是输入之一;生产级能力还包括权威属性、关系路径、默认拒绝、策略版本、缓存撤销与决策审计。