2. RBAC模型深度解析:四层模型详解与实战
好,咱们进入正题。RBAC,全称是Role-Based Access Control,基于角色的访问控制。这玩意儿在权限管理领域,就像数据库领域的SQL一样,是绕不开的基础。我做了这么多年SaaS架构,可以说90%的权限系统,底层都是RBAC的变体。
今天咱们就把RBAC的四个层次——RBAC0、RBAC1、RBAC2、RBAC3,掰开了揉碎了讲清楚。你想想看,搞懂了这些,你就能理解市面上绝大多数权限系统的设计思路了。
2.1 用户-角色-权限:铁三角关系建模
先看最核心的关系。说白了就是三张表:用户、角色、权限。它们之间的关系,我习惯用一句话概括:用户是主体,权限是客体,角色是桥梁。
具体建模时,我建议用这样的关系:
- 用户表:存用户基本信息,比如user_id、name、status。
- 角色表:存角色定义,比如role_id、role_name、description。
- 权限表:存具体的操作权限,比如permission_id、resource、action。
- 用户-角色关联表:多对多关系,一个用户可以有多个角色。
- 角色-权限关联表:多对多关系,一个角色可以有多个权限。
嗯,这里要注意一点:千万不要把权限直接挂到用户身上。我在项目中遇到过,有人图省事,直接在用户表里加了个permissions字段,存JSON数组。结果后来权限一多,查询性能直接崩了,改都改不动。
核心原则:用户只跟角色关联,角色才跟权限关联。这是RBAC的根基。
2.2 RBAC0:最基础的模型
RBAC0,就是上面说的那个铁三角。它定义了最核心的概念:用户、角色、权限,以及它们之间的多对多关系。
说白了,RBAC0就是告诉你:别把权限直接给用户,先建个角色,把权限给角色,再把角色给用户。就这么简单。
举个例子:
-- 创建角色
INSERT INTO roles (role_id, role_name) VALUES (1, '销售经理');
-- 给角色分配权限
INSERT INTO role_permissions (role_id, permission_id) VALUES (1, 101); -- 查看客户列表
INSERT INTO role_permissions (role_id, permission_id) VALUES (1, 102); -- 编辑客户信息
-- 把角色给用户
INSERT INTO user_roles (user_id, role_id) VALUES (1001, 1);
你看,用户1001就有了销售经理的所有权限。如果哪天要调整权限,改角色就行,不用一个个改用户。这就是RBAC0的价值。
我的经验:RBAC0适合小团队、权限结构简单的场景。比如几十个人的创业公司,用RBAC0完全够用。
2.3 RBAC1:引入角色继承
RBAC1在RBAC0的基础上,加了一个东西:角色继承。
为什么会需要这个?你想想看,一个公司里,销售总监和销售经理,权限肯定有重叠。销售总监能做的,销售经理基本也能做,只是总监多了几个高级权限。如果按RBAC0的做法,你得把公共权限在两个角色里各配一遍。麻烦不说,还容易出错。
RBAC1的解决方案是:让角色可以继承。比如:
- 销售经理:查看客户、编辑客户、创建订单
- 销售总监:继承销售经理的所有权限 + 删除客户、查看财务报表
建模时,需要在角色表里加一个parent_id字段:
CREATE TABLE roles (
role_id INT PRIMARY KEY,
role_name VARCHAR(50),
parent_id INT, -- 指向父角色
FOREIGN KEY (parent_id) REFERENCES roles(role_id)
);
查询权限时,需要递归查找父角色的权限。我记得有一次,我在项目中用递归CTE来实现这个逻辑,性能还不错。
避坑指南:我曾经在角色继承上踩过坑——继承层级太深,导致权限查询性能急剧下降。我的建议是:继承层级不要超过3层。超过3层,就该考虑是不是设计有问题了。
2.4 RBAC2:加入约束机制
RBAC2在RBAC0的基础上,加了各种约束。说白了,就是限制角色和用户的分配方式。
常见的约束有几种:
- 互斥角色:一个用户不能同时拥有两个互斥的角色。比如,不能同时是「采购员」和「审批员」,否则自己采购自己审批,容易出问题。
- 基数约束:一个角色最多能有多少个用户。比如,「超级管理员」这个角色,最多只能有2个人。
- 先决条件角色:要拥有角色A,必须先拥有角色B。比如,要当「项目经理」,必须先当过「开发工程师」。
这些约束怎么实现?我建议在分配角色时做校验:
-- 伪代码:检查互斥角色
function assignRole(userId, roleId):
// 获取该角色的互斥角色列表
exclusiveRoles = getExclusiveRoles(roleId)
// 检查用户是否已经拥有互斥角色
userRoles = getUserRoles(userId)
if any(role in userRoles for role in exclusiveRoles):
throw new Error("用户已拥有互斥角色,无法分配")
// 执行分配
insertUserRole(userId, roleId)
核心要点:RBAC2的约束,本质上是为了满足合规和安全要求。比如金融系统里,交易员和风控员必须是互斥角色,这是监管要求。
2.5 RBAC3:集大成者
RBAC3 = RBAC1 + RBAC2。它既有角色继承,又有各种约束。
说白了,RBAC3就是最完整的RBAC模型。它支持:
- 用户-角色-权限的基本关系
- 角色之间的继承
- 各种分配约束
在实际项目中,我很少见到完全按照RBAC3实现的系统。为什么?因为太复杂了。角色继承和约束同时存在时,权限计算的复杂度会指数级上升。
举个例子:角色A继承角色B,但角色A和角色C是互斥的。那用户能不能同时拥有角色B和角色C?这就要看具体的业务规则了。处理不好,就容易出逻辑漏洞。
我的建议:除非你的系统真的需要这么复杂的权限模型,否则不要轻易上RBAC3。大多数场景下,RBAC0加上一些简单的约束,就够用了。
2.6 四层模型对比总结
| 模型 | 核心特性 | 适用场景 | 复杂度 |
|---|---|---|---|
| RBAC0 | 用户-角色-权限基本关系 | 小团队、简单权限 | 低 |
| RBAC1 | 角色继承 | 层级分明的组织 | 中 |
| RBAC2 | 约束机制 | 合规要求严格的系统 | 中 |
| RBAC3 | 继承 + 约束 | 大型复杂系统 | 高 |
好了,RBAC的四层模型就讲到这里。下一章,咱们聊聊实际项目中,怎么根据业务需求选择合适的RBAC模型,以及一些常见的坑。记住,没有最好的模型,只有最合适的模型。