3. RBAC96模型家族:从基础到完整的权限控制体系

RBAC96 这名字听起来挺学术,其实它就是一套权限模型的标准化族谱。1996 年 Sandhu 等人提出的,所以叫 96。说白了,它把权限控制拆成了四个递进等级:RBAC0 打底,RBAC1 加层级,RBAC2 加约束,RBAC3 把前面俩揉在一起。

我刚接触这套东西的时候,以为 RBAC1 就是比 RBAC0 高级,后来在项目中踩过坑才发现——不是升级关系,是正交组合。你完全可以只用 RBAC0 加约束,不需要层级。选哪个,取决于业务复杂度。

3.1 RBAC0:最基础的用户-角色-权限模型

RBAC0 是地基。它定义了三个核心要素:用户、角色、权限。用户通过角色获得权限,角色是权限的集合。就这么简单。

我早期做的一个企业内部系统,用的就是 RBAC0。二十几个角色,每个角色绑定一组菜单和操作权限。用户登录时查角色,角色查权限,搞定。那时候觉得权限管理不过如此。

核心关系:

  • 用户 ↔ 角色:多对多。一个用户可以有多个角色,一个角色可以分配给多个用户。
  • 角色 ↔ 权限:多对多。一个角色可以有多个权限,一个权限可以出现在多个角色中。

你们可能会问:那用户能不能直接拥有权限?在 RBAC0 里,不行。权限必须通过角色授予。这是 RBAC 的基石,也是它的设计哲学——权限管理要集中,不能散落到用户级别。

要素 说明 我遇到的坑
用户 (User) 系统使用者,如员工、管理员 别把用户组和角色混在一起,分开设计
角色 (Role) 权限的容器,如"财务主管"、"普通员工" 角色名要有业务含义,别叫 role_001
权限 (Permission) 对资源的具体操作,如"查看订单"、"删除用户" 粒度要一致,别有的精细到按钮,有的粗到模块

3.2 RBAC1:引入角色层级,解决职权晋升问题

RBAC0 用了一阵子,问题来了:公司有组织架构,角色之间有上下级关系。比如"财务总监"天然继承"财务经理"的所有权限,再加一些高级权限。如果每次都在 RBAC0 里分别配,累死人。

RBAC1 引入了一个概念:角色继承。子角色自动拥有父角色的所有权限。这里要注意——继承是单向的,通常是上级继承下级(或者说下级继承上级,看你怎么定义方向)。

我的习惯:角色层级画成树状图。根节点是"超级管理员",叶子节点是"实习生"。每次新增角色,先看它挂在哪一级下面,继承关系自动搞定。不过要注意,不要搞出循环继承——A 继承 B,B 继承 C,C 又继承 A,别笑,我真见过有人这么设计。

RBAC1 的继承有两种模式:

  • 一般继承:角色 A 继承角色 B 的所有权限,同时可以增加自己的权限。
  • 受限继承:角色 A 只能继承某些指定权限,不能全部继承(用得少,但某些合规场景需要)。

3.3 RBAC2:加约束,让权限分配更安全

RBAC2 不改变 0 和 1 的结构,而是给角色分配加上了约束条件。为什么要约束?因为权限过大或角色冲突是安全大忌。

最常见的约束有三种:

  1. 互斥角色(Mutual Exclusion):一个用户不能同时拥有两个冲突的角色。比如"采购员"和"验收员"不能是同一个人——这是内控基本要求。我曾经在金融项目里,就因为没配互斥,被审计揪出来过。
  2. 基数约束(Cardinality):角色可分配的用户数量上限。比如"CEO"角色只能有 1 个人。别小看这个,有些系统忘了限制,结果出现三个 CEO 同时审批……
  3. 先决条件约束(Prerequisite):用户必须先有角色 A,才能申请角色 B。比如"项目经理"必须先是"高级工程师"。这个在晋升流程里很实用。

警告:约束是运行时检查的,不是设计时的。如果你在代码里硬编码了互斥逻辑,改一次业务规则就要改代码,那你就输了。把约束做成配置项,让业务人员自己管理。

3.4 RBAC3:整合所有能力,1+1+1 > 3

RBAC3 就是 RBAC1 + RBAC2。既有层级继承,又有约束规则。这是实际生产中最常用的模型,也是大多数商用权限系统的底层逻辑。

但别以为用了 RBAC3 就万事大吉。我做过一个 SaaS 平台,客户要求支持多租户,每个租户内部又有自己的角色层级和约束。RBAC3 不够用,得在它上面再套一层租户隔离。这就是 ABAC 登场的地方——不过那是后面章节的内容。

来看一张家族关系图,把 RBAC96 四个模型串起来理解:

RBAC96 模型家族关系图 RBAC96 模型家族关系 RBAC0 用户-角色-权限 基础 RBAC1 角色层级(继承) RBAC2 约束(互斥/基数/前提) RBAC3 RBAC1 + RBAC2 整合 RBAC3 并非取代 RBAC0,而是叠加了层级的继承能力和约束的安全规则

关键结论:

  • RBAC0 是必须实现的基座。
  • RBAC1 解决角色过多、权限重复分配的问题。
  • RBAC2 解决安全合规、职责分离的问题。
  • RBAC3 是大多数重量级系统的标配。

3.5 实际项目中的选型建议

别一上来就上 RBAC3。我就吃过这个亏——小团队,几十个人,搞了复杂的角色层级加约束,结果光维护角色继承树就花了两周。后来我总结了一个简单的选型原则:

  • 小于 50 个角色,无组织层级:RBAC0 足够。数据库三张表(用户、角色、用户角色关联)搞定。
  • 有明确的上下级管理关系:加 RBAC1。比如科长继承科员的权限,处长继承科长的权限。
  • 有合规审计需求(SOX、GDPR 等):加 RBAC2。互斥、卡基数、卡前置条件。
  • 大型企业、多部门、多层级、强合规:RBAC3 走起。

曾经有一个客户,非要把 RBAC3 的所有特性都用上。结果角色层级画了五层,互斥规则配了三十多条,每次新增一个角色都要走审批流程。业务人员烦死了,最后只保留了 RBAC0+互斥约束。所以说,模型选型要克制,够用就行。

3.6 代码层面怎么设计?

我不打算贴完整的代码,但给你一个核心数据表设计思路,RBAC3 的数据库模式大致长这样:

-- RBAC3 核心表
CREATE TABLE users (
    user_id    INT PRIMARY KEY,
    username   VARCHAR(50) NOT NULL
);

CREATE TABLE roles (
    role_id    INT PRIMARY KEY,
    role_name  VARCHAR(50) NOT NULL,
    parent_role_id INT,              -- RBAC1 层级继承
    max_users  INT DEFAULT NULL      -- RBAC2 基数约束
);

CREATE TABLE permissions (
    perm_id    INT PRIMARY KEY,
    perm_code  VARCHAR(50) NOT NULL, -- 如 'order:view'
    perm_desc  VARCHAR(200)
);

-- 多对多关系表
CREATE TABLE user_roles (
    user_id INT REFERENCES users(user_id),
    role_id INT REFERENCES roles(role_id),
    PRIMARY KEY (user_id, role_id)
);

CREATE TABLE role_permissions (
    role_id INT REFERENCES roles(role_id),
    perm_id INT REFERENCES permissions(perm_id),
    PRIMARY KEY (role_id, perm_id)
);

-- 互斥约束表(RBAC2)
CREATE TABLE role_mutex (
    role_id_a INT REFERENCES roles(role_id),
    role_id_b INT REFERENCES roles(role_id),
    PRIMARY KEY (role_id_a, role_id_b)
);

-- 先决条件表(RBAC2)
CREATE TABLE role_prerequisite (
    role_id       INT REFERENCES roles(role_id),
    prerequisite_role_id INT REFERENCES roles(role_id),
    PRIMARY KEY (role_id, prerequisite_role_id)
);

你看,其实 RBAC3 在数据库层面就多了几行约束表。权限判定时,递归查父角色权限,然后检查互斥和基数——这部分逻辑要注意性能,别每个请求都递归一次。我常用的做法:用户登录时,一次性算出他拥有的所有权限(包括继承的),缓存到 session 或 Redis 里。

避坑指南:我曾经在项目里直接用递归 SQL 查询角色继承链,上线后部门主管角色有 6 层继承,每次权限检查耗时 200ms。后来改用 closure table(闭包表)或者预计算扁平化权限,性能直接降到 2ms。