3. RBAC0 模型:基础模型的结构与用户-角色-权限映射
讲权限设计,RBAC0 是绕不过去的基石。说白了,它就是一套最经典的“用户 → 角色 → 权限”三层结构。很多新手一开始总想直接搞复杂的权限模型,结果把自己绕进去了。我个人的习惯是,无论项目多大多小,先把 RBAC0 这张白纸画清楚,后面再叠加什么约束、层级,都会顺手很多。
3.1 三个核心要素:用户、角色、权限
我们先拆开来看。这三样东西,你理解清楚了,RBAC0 就懂了一大半。
- 用户(User):系统的操作主体,可以是人、服务账号、或者第三方应用。
- 角色(Role):一组权限的集合,是一个“职位”或“身份”的抽象。比如“内容编辑”、“订单管理员”。
- 权限(Permission):对某个资源的某种操作。比如“删除文章”、“导出订单列表”。
你想想看,如果没有角色这一层,我们要给每个用户单独分配权限。1000 个编辑,就得分配 1000 次。某天新增一个“批量发布”的权限,还得挨个给这 1000 人加上。累不累?角色就是用来解决这个问题的。
核心映射关系:
- 用户 ↔ 角色:多对多。一个用户可以有多个角色,一个角色也可以属于多个用户。
- 角色 ↔ 权限:多对多。一个角色拥有多个权限,一个权限也可以被多个角色拥有。
最终,用户通过“持有角色”,间接获得“角色的所有权限”。
3.2 RBAC0 的标准结构图
下面这张 SVG 图,是我在准备这个章节时专门画的。它能帮你一眼看清用户、角色、权限之间的三层关系。嗯,这里要注意,箭头方向代表“归属”或“拥有”。
我从这张图里总结出一个经验:“角色”是整条链子的枢纽。权限的增减、用户的归属,都应该绕着角色转,而不是直接改用户或权限表。
3.3 两种映射关系详解
掌握 RBAC0,其实就是掌握两个“多对多”映射。我在项目中见过太多人把它们混为一谈,导致后期数据维护非常痛苦。我们一个一个说。
3.3.1 用户-角色映射
它解决的是“谁是什么身份”的问题。举个例子:
- 用户“张三”被授予了“内容编辑”和“内审员”两个角色。
- 系统在做权限判定时,会把这两个角色下的所有权限合并起来。
数据库表结构通常是这样:
-- 用户与角色的关系表
CREATE TABLE user_role (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL COMMENT '用户ID',
role_id BIGINT NOT NULL COMMENT '角色ID',
UNIQUE KEY uk_user_role (user_id, role_id)
);
说白了,你给一个用户分配多个角色,实际上就是在“累加”权限。这一点非常重要,也是后面讲角色互斥(SSD)时会反复提到的点。
3.3.2 角色-权限映射
它解决的是“角色能干什么”的问题。每个角色背后都是一张“权限清单”。
我曾经接手过一个老项目,他们直接把权限写死在角色表的某个 JSON 字段里。你说这多可怕?要统计“谁有导出权限”,只能全表扫描解析 JSON。所以,我建议一定要用关系表来存:
-- 角色与权限的关系表
CREATE TABLE role_permission (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
role_id BIGINT NOT NULL COMMENT '角色ID',
permission_id BIGINT NOT NULL COMMENT '权限ID',
UNIQUE KEY uk_role_permission (role_id, permission_id)
);
一个小技巧: 在设计角色-权限表时,可以额外加一个 grant_type 字段,值为 1 表示“允许”,0 表示“拒绝”。虽然 RBAC0 标准模型不直接支持“负权限”,但这个字段为你后期做“白名单 + 黑名单”的权限覆盖策略留了后路。我私下在好几个项目中都这么玩,效果不错。
3.4 RBAC0 的典型流程
- 用户登录:系统验证身份,拿到用户 ID。
- 查询角色:根据用户 ID,在
user_role表中查出该用户拥有的所有角色 ID。 - 合并权限:根据角色 ID 列表,在
role_permission表中查出所有相关的权限 ID。注意是 “并集”。 - 缓存结果:通常把最终合并的权限集合缓存起来,避免每次请求都查库。
- 执行决策:当用户尝试操作某资源时,系统校验其拥有的权限集合中是否包含所需权限。
这个流程非常清晰。你想想看,它把用户和权限完全解耦了。中间隔了一层“角色”,后续不管是新增用户、调整权限,还是批量操作,都只需要改动映射表,完全不影响另一端的结构。
3.5 表格总结:RBAC0 的关键特征
| 维度 | 说明 | 对设计的影响 |
|---|---|---|
| 用户与角色 | 多对多映射 | 一个用户可身兼多职;一个角色可包含多个用户 |
| 角色与权限 | 多对多映射 | 权限的分配与回收,只跟角色挂钩 |
| 无约束 | 不支持角色互斥、层级继承 | 简单易用,适合大部分中小型系统 |
| 最小权限原则 | 可以通过拆细角色来近似实现 | 需要人工保证角色内权限不冗余 |
3.6 避坑指南
我曾经遇到过一个“经典”案例:
有个后台系统,为了图方便,把“菜单可见”和“操作按钮可见”混在同一个权限 ID 里。结果呢?一个角色如果赋予了“查看文章”的权限,它就自动拥有了“查看文章列表”的菜单权限。后来产品说要隐藏某个菜单项,只保留查看文章的功能,结果改不动。因为权限粒度太粗了。
教训: 权限的粒度一定要够细。最稳的做法是:一个权限只代表 “对某个资源的某一种操作”,比如 article:create、article:delete,而不是 article:all。
另外,请务必记住:不要在 RBAC0 阶段试图解决所有问题。角色继承、职责分离、数据权限这些,是 RBAC1、RBAC2、RBAC3 要干的活。我们先把基础打牢,后面再慢慢加特性。