权限模型设计:RBAC 与 ABAC
权限这块,很多团队一开始都会选 RBAC,因为简单直观。但一旦业务复杂起来,角色数量爆炸,你就会发现 RBAC 不够用了。我这些年做过的安全集成项目里,至少有一半最后都不得不引入 ABAC 做补充。
说白了,RBAC 是“你是谁”决定你能干什么,ABAC 是“你 + 环境 + 资源”一起决定。咱们今天就把这两个模型拆开看透,再聊聊怎么把策略引擎嵌进去。
RBAC:角色就是通行证
RBAC 的核心思路很朴素:
- 用户 → 角色 → 权限
- 一个角色对应一组权限集合
- 用户加入角色,就继承了权限
我记得早年做企业内部系统时,最常见的角色就是“管理员”、“编辑”、“访客”。每个角色背后是一个权限矩阵,直接在代码里写死。后来开始用策略引擎,才把权限抽离出来。
RBAC 的优点
- 模型简单,开发理解成本低
- 权限分配快,改角色即可批量改权限
- 审计直观,谁有什么角色一目了然
但坑也明显。我遇到过一家公司,业务线 20 多条,角色数量飙升到 300 多个。一个运营需要“内容编辑 + 数据查看 + 活动管理”,结果他们创建了“运营-内容-数据-活动”复合角色,名字长得像八宝粥。这种时候 RBAC 就变成了角色爆炸。
避坑:角色爆炸
我曾经在一个 SaaS 项目里接手过 400 多个角色。查下来,三分之一是重复的。团队为了省事,每来一个新需求就新建角色,从不复用。最后我用 RBAC 分层 + 角色继承才压到 80 个。记住:角色数量超过 50 个就要警惕,超过 100 个必须分层。
ABAC:用属性说话
ABAC 不做角色,而是用属性来评估。属性来自三方面:
- 用户属性:部门、职级、区域、安全等级
- 资源属性:文档分类、敏感度、创建时间
- 环境属性:时间、IP、设备类型、网络位置
策略引擎收到请求后,把这三组属性拿过来算一下,符合策略就放行,不符合就拒绝。
举个例子:一个文档系统里,策略可以写成“只有研发部的人,在工作时间(9:00-18:00),从公司内网访问,且文档敏感度≤2,才能查看”。这在 RBAC 里得拆成好几个角色和条件,而在 ABAC 里就是一条策略。
RBAC vs ABAC:对比一下
| 维度 | RBAC | ABAC |
|---|---|---|
| 授权粒度 | 粗,基于角色 | 细,基于属性组合 |
| 策略动态性 | 差,修改角色需审批 | 强,属性变化即时生效 |
| 管理复杂度 | 低(角色少时) | 高(需维护属性定义和策略) |
| 适用场景 | 组织架构稳定、权限类型少 | 多维度条件、动态环境、合规要求高 |
| 性能开销 | 低,角色到权限的映射简单 | 中等,需解析属性并匹配策略 |
| 审计难度 | 容易,看角色即可 | 需要记录属性上下文,否则看不清 |
我的建议
别上来就全上 ABAC。先评估你的业务场景:如果权限规则 80% 可以靠角色搞定,那就用 RBAC 打底,再在需要细粒度的地方引入 ABAC 做补充。我做过的一个金融项目就是 RBAC+ABAC 混合:角色控制菜单访问,ABAC 控制数据行级权限。
策略引擎怎么集成?
权限模型定好后,关键是把策略引擎插进系统架构。现在主流思路是 PDP(Policy Decision Point) 和 PEP(Policy Enforcement Point) 分离。
- PEP:放在 API 网关或应用层,拦截请求,问 PDP “这个人能不能做这个操作?”
- PDP:独立服务或组件,加载策略,计算结果(允许/拒绝),返回给 PEP
- PAP(Policy Administration Point):管理界面,给管理员写策略
- PIP(Policy Information Point):提供属性数据,比如从 LDAP 取用户部门,从 CMDB 取资源标签
我习惯用 Open Policy Agent(OPA) 做 PDP,配合 Rego 语言写策略。下面是一个简单的 Rego 策略示例,要求用户必须是研发部,访问时间在工作日内,且资源标签不能是“机密”:
package security.access
default allow = false
# 获取用户部门
user_dept := input.user.attributes.department
# 获取资源敏感标签
res_label := input.resource.metadata.label
# 当前时间(假设 PEP 传入 UTC 小时)
current_hour := time.now_ns() / (1000 * 1000 * 1000) % 86400 / 3600
allow {
user_dept == "R&D"
current_hour >= 9
current_hour < 18
res_label != "confidential"
# 工作日判断省略…
}
这个策略在 OPA 里跑起来就是一个 HTTP 接口。PEP(比如 NGINX 网关)发 POST 请求,带上用户属性、资源属性、动作,OPA 返回 true/false。集成很简单,但属性数据的质量和实时性会直接影响判断。我曾经踩过坑:PIP 从老旧的 LDAP 拿部门信息,更新延迟 2 小时,导致新入职员工访问被拒。后来加了缓存和异步同步才解决。
架构层面怎么落?
下面这张图是我在一个实际项目中用的权限集成架构。PEP 部署在网关层,PDP 独立部署,PIP 从多个数据源聚合属性。
实际集成时,PEP 和 PDP 之间走的是同步 HTTP 还是异步 gRPC,取决于性能要求。我个人偏向同步,因为权限判定必须实时返回,不能等。但要注意超时:如果 PDP 挂掉,PEP 得有一个 fallback 策略(比如静默拒绝或缓存最近结果)。
混合模型怎么设计?
你想想看,完全用 ABAC 取代 RBAC 并不划算。大部分场景下,用户的角色仍然是最强信号。我推荐分层权限模型:
- 第一层:粗粒度 RBAC — 控制功能模块、菜单、大操作(增删改查)。
- 第二层:细粒度 ABAC — 控制数据行、字段、特定条件(比如仅本人创建的数据)。
策略引擎里怎么融合?很简单:先判断 RBAC 角色有没有权限,如果有,再交给 ABAC 策略做二次过滤。效率上,大部分请求在第一层就会因为角色不匹配直接拒绝,不会走到 ABAC 属性解析。
现实案例
我做过的一个医疗信息系统就是这样:医生角色可以访问患者病历,但 ABAC 策略进一步限制——只能访问自己负责的患者。如果只靠 RBAC,就得每个医生挂一个专属角色,那角色数就变成医生数了,显然不现实。
避坑总结
- 属性数据质量是第一命门。PIP 拿到的属性不准,策略再完美也没用。我以前吃过亏,用户离职后 LDAP 没及时更新,导致已离职员工还能用旧角色访问系统。
- 策略粒度别太细。我曾见过有人把每个文件的每个字段都写成一条 ABAC 策略,策略库上万条,性能惨不忍睹。保持“够用即可”原则。
- 审计日志必须包含属性上下文。RBAC 只要记“用户+角色+操作”就行,ABAC 还得记“当时用户属性、资源属性、环境属性”,否则事后排查根本看不懂为什么拒绝。
- 灰度切换。从 RBAC 迁移到 ABAC 不要一刀切。我建议先让新功能走 ABAC,老功能维持 RBAC,同时接入双写审计,比较两套模型的结果差异,确认无误后再全量切。
权限模型不是非此即彼,RBAC 和 ABAC 是战友不是对手。关键是结合你的业务场景、属性丰富度、性能要求,设计出刚好能用的方案。