4、权限隔离基础:权限模型概述、RBAC模型详解、ABAC模型详解
权限隔离这件事,说白了就是解决一个问题:谁,能对什么资源,做什么操作。
我早年做第一个企业级项目时,权限这块写得特别糙——直接在代码里写死 if (user.role === 'admin')。结果呢?上线第三天,产品经理就拿着新需求来找我:「我们要加一个超级运营角色,只能看订单不能改价格。」我一看代码,得,全得重构。从那以后,我再也不敢小看权限模型的设计了。
4.1 权限模型概述
权限模型,就是一套描述「主体-资源-操作」关系的规则框架。常见的模型有这么几种:
- ACL(访问控制列表):直接给每个用户或每组用户挂资源权限列表。简单粗暴,但维护成本高。
- RBAC(基于角色的访问控制):用户不直接关联权限,而是通过「角色」这个中间层来关联。这是目前最主流的方案。
- ABAC(基于属性的访问控制):根据用户、资源、环境的多维属性动态计算权限。灵活,但实现复杂。
- PBAC(基于策略的访问控制):把权限抽象成策略规则,通常与ABAC结合使用。
你想想看,ACL就像给每个员工单独发一把钥匙,人一多钥匙就乱套。RBAC则是按岗位发门禁卡——你是财务就给财务卡,你是运维就给运维卡。ABAC更高级,它不光看你是谁,还看你现在在哪儿、什么时间、用什么设备。
核心观点:没有最好的模型,只有最合适的模型。小型系统用RBAC足够,大型分布式系统往往需要RBAC+ABAC混合使用。
4.2 RBAC模型详解
RBAC(Role-Based Access Control)是1992年由David Ferraiolo和Richard Kuhn提出的。我接触的第一个权限框架Apache Shiro用的就是RBAC思想。说实话,这个模型能火三十年,不是没道理的。
4.2.1 RBAC的核心要素
| 要素 | 说明 | 举例 |
|---|---|---|
| 用户(User) | 系统操作的主体 | 张三、李四 |
| 角色(Role) | 权限的集合,用户与权限的桥梁 | 管理员、运营、审计 |
| 权限(Permission) | 对资源的操作许可 | 创建订单、删除用户 |
| 会话(Session) | 用户激活角色的过程 | 用户登录后选择角色 |
4.2.2 RBAC的三种模型变体
RBAC标准里其实分了几个层级。我习惯把它们理解成「基础版」「进阶版」和「完全版」。
- RBAC0(基础模型):用户-角色-权限,最简单的三角关系。一个用户可以有多个角色,一个角色可以有多个权限。
- RBAC1(角色继承模型):角色之间可以继承。比如「超级管理员」继承「普通管理员」的所有权限,再加一些高级权限。这能减少重复配置。
- RBAC2(角色约束模型):增加了约束规则。比如「互斥角色」——一个人不能同时担任财务和审计;「基数约束」——一个角色最多只能有5个人。
我的经验:实际项目中,RBAC0就够用了。角色继承和约束虽然听起来很美好,但一旦业务复杂起来,角色层级深了,排查权限问题会非常痛苦。我曾经在一个项目里看到角色继承链长达7层,最后没人能说清楚某个权限到底是从哪一层继承来的。
4.2.3 RBAC的数据库设计
嗯,这里直接给出一套经典的五表设计。我几乎在每个项目里都用这个模板:
-- 用户表
CREATE TABLE `sys_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
);
-- 角色表
CREATE TABLE `sys_role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`role_name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
);
-- 权限表
CREATE TABLE `sys_permission` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`perm_code` varchar(100) NOT NULL,
`perm_name` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
);
-- 用户-角色关联表
CREATE TABLE `sys_user_role` (
`user_id` bigint(20) NOT NULL,
`role_id` bigint(20) NOT NULL,
PRIMARY KEY (`user_id`, `role_id`)
);
-- 角色-权限关联表
CREATE TABLE `sys_role_permission` (
`role_id` bigint(20) NOT NULL,
`permission_id` bigint(20) NOT NULL,
PRIMARY KEY (`role_id`, `permission_id`)
);
这套设计的核心思想就是「解耦」。用户和角色分开,角色和权限分开。哪天产品说「给运营加一个导出报表的权限」,你只需要在角色-权限表里加一条记录,完全不用动用户数据。
4.2.4 RBAC的优缺点
| 优点 | 缺点 |
|---|---|
| 模型简单,容易理解和实现 | 角色数量膨胀后管理困难 |
| 权限变更只需调整角色 | 细粒度控制不足(比如只能按部门限制) |
| 符合企业组织架构直觉 | 无法处理动态条件(如时间、地点) |
| 性能好,权限判断是O(1)级别 | 角色爆炸时维护成本急剧上升 |
4.3 ABAC模型详解
ABAC(Attribute-Based Access Control)是解决RBAC「不够灵活」这个痛点而生的。它不再依赖固定的角色,而是根据一组属性规则来动态判断权限。
举个例子:RBAC里,你给「运营」角色赋予了「查看订单」的权限。那所有运营都能看所有订单。但业务说:「运营只能看自己负责的区域的订单。」用RBAC怎么搞?要么给每个区域建一个角色(华东运营、华北运营……),要么在代码里硬编码判断。这两种方式,我都踩过坑。
ABAC的解法是:定义一条策略规则——用户.部门 == 订单.区域。谁满足这个条件,谁就能看。不需要建一堆角色。
4.3.1 ABAC的四类属性
- 主体属性(Subject Attributes):用户的身份信息,如年龄、部门、职级、安全等级。
- 资源属性(Resource Attributes):被访问对象的特征,如文档密级、创建者、所属项目。
- 环境属性(Environment Attributes):访问发生时的上下文,如当前时间、IP地址、设备类型。
- 操作属性(Action Attributes):要执行的操作类型,如读取、写入、删除、导出。
ABAC的核心公式:
权限 = 策略引擎(主体属性, 资源属性, 环境属性, 操作属性) → 允许/拒绝
4.3.2 ABAC的策略定义示例
假设我们有一个文档管理系统,需要实现这样的规则:「只有文档所属部门的员工,在工作时间(9:00-18:00)才能编辑该文档。」用ABAC的策略语言来表达:
{
"effect": "允许",
"条件": {
"所有条件必须满足": [
{
"主体.部门": "等于",
"资源.所属部门"
},
{
"环境.当前时间": "在范围内",
"值": ["09:00", "18:00"]
},
{
"操作.类型": "等于",
"值": "编辑"
}
]
}
}
你看,这里没有出现任何具体的用户名或角色名。规则是通用的,谁满足条件谁就能操作。这就是ABAC的魅力。
4.3.3 ABAC的实现架构
我参与过一个大型SaaS平台的权限重构,最终选型就是ABAC。它的架构大致分为三层:
- 策略管理点(PAP):负责定义和管理策略规则。通常是一个后台管理界面。
- 策略决策点(PDP):核心引擎,接收请求,根据策略计算是否允许。这是最复杂的部分。
- 策略执行点(PEP):拦截请求,调用PDP做决策,然后执行允许或拒绝。
下面这张图展示了ABAC的完整决策流程:
4.3.4 ABAC的优缺点
| 优点 | 缺点 |
|---|---|
| 粒度极细,可以精确到单个资源实例 | 策略引擎性能开销大,复杂规则下延迟高 |
| 无需预定义角色,动态适应业务变化 | 策略管理复杂,非技术人员难以理解 |
| 支持多维条件组合(时间、地点、设备等) | 调试困难,权限问题排查成本高 |
| 适合多租户、大规模分布式系统 | 需要专门的策略语言和引擎(如XACML、OPA) |
避坑指南:我曾经在一个项目中过度使用ABAC,把几十条策略规则堆在一起。结果有一次线上排查一个用户为什么看不到某个报表,花了整整两天。最后发现是一条环境属性规则写错了——把「当前时间在09:00-18:00」写成了「当前时间在09:00-18:00之外」。所以我的建议是:能用RBAC解决的场景,不要硬上ABAC。ABAC适合那些权限规则频繁变化、条件复杂的场景。
4.4 RBAC vs ABAC:如何选择
很多团队在选型时会纠结。我一般会问三个问题:
- 你们的角色数量稳定吗?如果角色不超过20个,且很少变化,RBAC就够了。
- 权限判断需要依赖上下文吗?比如「只能在工作时间访问」「只能查看本部门数据」——有这类需求,考虑ABAC。
- 团队有精力维护策略引擎吗?ABAC需要引入OPA、Casbin这类框架,学习曲线不低。
实际项目中,我更推荐混合方案:用RBAC做粗粒度控制(比如「这个用户是运营角色」),用ABAC做细粒度过滤(比如「运营只能看华东区的订单」)。这样既保留了RBAC的简洁性,又获得了ABAC的灵活性。
我的习惯:先按RBAC把架子搭起来,等业务确实出现「角色爆炸」或「动态条件判断」的需求时,再在关键路径上引入ABAC策略。不要一开始就追求「完美」的权限模型——你永远猜不到产品经理下一步会提出什么需求。