4. RBAC 进阶:角色继承、角色互斥、角色会话
做权限设计,光有基础的 RBAC(用户-角色-权限)够不够?
说实话,小公司还行。一旦上了规模,你会遇到很多棘手的问题。比如一个“市管理员”权限为啥要比“省管理员”小?一个用户能不能同时当“会计”和“出纳”?用户登录后的权限状态怎么管理?
这些问题,基础 RBAC 搞不定。我们得聊聊它的进阶版。
4.1 角色继承:让权限“长”出层级
什么叫角色继承?说白了,就是“儿子”拥有“老子”的所有权限,还能额外加点自己的。
我举个例子。你做一个企业内部系统。有“普通员工”、“部门经理”、“总监”。一般情况下,“总监”的权限应该包含“部门经理”的权限,“部门经理”的权限又包含“普通员工”的权限。
如果你用基础 RBAC,需要把“查看员工信息”、“审批请假”这些权限,在每一个角色里都重新关联一遍。
这太蠢了。 数据冗余大,维护成本高,还容易出错。
角色继承就是为了解决这个问题。它分两种:
- 通用继承:子角色自动获得父角色的所有权限。这是最常见的。
- 受限继承:子角色只能继承部分父角色权限,或者可以“覆盖”父角色的某些权限。
它的实际应用场景非常清楚:
| 业务场景 | 继承关系 | 举例 |
| 组织层级 | 上级角色继承下级角色 | 总监 > 经理 > 员工 |
| 项目角色 | 高级角色继承基础角色 | Project Lead > Developer > Tester |
| 区域管理 | 全局管理员继承区域管理员 | 全国管理员 > 大区管理员 > 城市管理员 |
4.2 角色互斥:让权力不能“一手遮天”
你想想看,一个人既做采购,又做验货,是不是很危险?
这就是角色互斥要解决的。它不允许同一个用户同时拥有两个 互斥的 角色。
为什么要搞这个?
为了 职责分离(Separation of Duties)。这是安全审计的基本要求。它分两种实现方式:
- 静态互斥(SSD):在用户分配角色时直接限制。你给张三分配了“会计”角色,就不能再给他分配“出纳”角色了。系统直接拒绝。
- 动态互斥(DSD):用户可以被分配两个互斥角色,但不能在同一个会话中同时激活它们。比如你既是“会计”又是“出纳”,但你不能在同一个登录会话里同时使用两种权限。
我个人觉得,静态互斥更严格,也更安全。动态互斥更多是业务灵活性的妥协。下面这个表能帮你快速对比:
| 特性 | 静态互斥(SSD) | 动态互斥(DSD) |
| 限制时间点 | 角色分配时 | 角色激活时(会话) |
| 灵活度 | 低 | 中 |
| 实现复杂度 | 低 | 中 |
| 适用场景 | 强安全合规 | 灵活业务流转 |
- 金融系统:交易员不能同时是审核员。这是静态互斥。
- 电商后台:商品上架员不能同时是商品审核员。这是静态互斥。
- 实验环境:开发者不能同时在同一个会话里拥有“管理员”和“普通用户”角色。这是动态互斥。
4.3 角色会话:用户权限的“活动状态”
基础 RBAC 里,用户、角色、权限的关系是静态的。但用户登录后,他的权力是“活”的。这个“活”的状态,就是角色会话。
角色会话,记录了用户在 当前登录时间内,激活了哪些角色。
你想想看,一个用户可能有 10 个角色。但他一次只能干一件事。他没必要同时拥有 10 个角色的所有权限。
所以,会话的作用就是 限定 用户“当前”能干什么。
- 单一会话:一个用户一次登录只能激活一个角色。切换角色需要重新登录。
- 多角色会话:一个用户一次登录可以同时激活多个角色。用户拥有这些角色的权限的“并集”。
怎么在代码里实现呢?我讲一个最朴素的思路:
// 伪代码:角色会话管理
class RoleSession {
constructor(userId) {
this.userId = userId;
this.activeRoles = []; // 当前会话已激活的角色列表
}
// 激活一个角色
activateRole(role) {
// 检查角色互斥(动态互斥)
if (this.isExclusive(role)) {
throw new Error("角色互斥,无法激活");
}
this.activeRoles.push(role);
}
// 检查角色是否互斥
isExclusive(role) {
// 遍历已激活的角色,检查是否有与当前角色互斥的
for (let activeRole of this.activeRoles) {
if (activeRole.mutualExclusiveRoles.includes(role.id)) {
return true;
}
}
return false;
}
// 获取当前用户拥有的所有权限(从已激活角色中汇总)
getPermissions() {
let permissions = [];
for (let role of this.activeRoles) {
for (let perm of role.permissions) {
if (!permissions.includes(perm)) {
permissions.push(perm);
}
}
}
return permissions;
}
}
这个会话设计非常重要。它让你能精细控制用户 同一时间 能做什么。很多安全漏洞,就是因为没有管理好会话中的角色。
4.4 三者的协作关系:一张图讲明白
你可能会问,这三个东西怎么一起用?它们不打架吗?
不会。它们是分工合作的。我用一张图来表示它们的关系:
这张图的关键在于:
- 用户 首先通过 角色继承 获得所有可能的角色。
- 角色互斥 作为一道防火墙,阻止用户获得冲突的角色。
- 角色会话 是用户登录后,实际激活的角色集合。它受互斥约束。
- 最终,用户的 权限 是会话中所有激活角色权限的并集。
你有没发现,这个模型就像一个三明治:继承和互斥是静态层,会话运行态,权限是最终结果。它们各司其职,非常清楚。
嗯,到这里你应该能理解,为什么复杂的业务系统一定要用这些进阶特性了。它们不是理论玩具,是保障权限系统安全、灵活、可扩展的基石。