2. RBAC核心模型(RBAC0):用户、角色、权限三要素详解
RBAC0 是 RBAC 体系中最基础的模型,也是所有权限系统的起点。它定义了三个核心实体:用户(User)、角色(Role)、权限(Permission),以及它们之间的两层关联关系。理解 RBAC0,就等于掌握了权限设计的底层逻辑。
2.1 三要素定义
| 要素 | 英文 | 定义 | 典型示例 |
|---|---|---|---|
| 用户 | User | 系统的操作主体,通常对应一个人或一个服务账号 | 张三(工号 1001)、客服机器人 |
| 角色 | Role | 权限的集合,代表一类职责或岗位 | 管理员、运营编辑、普通访客 |
| 权限 | Permission | 对某个资源执行某种操作的许可 | 「创建订单」「删除文章」「查看报表」 |
核心原则:用户不直接与权限挂钩,而是通过「角色」作为中间层间接获得权限。这一层抽象是 RBAC 灵活性的根源。
2.2 用户 ↔ 角色 ↔ 权限 的关联关系
RBAC0 的关联关系可以概括为两条线:
- 用户-角色关联(User-Role Assignment, UA):一个用户可以拥有多个角色,一个角色也可以被多个用户持有。这是多对多关系。
- 角色-权限关联(Role-Permission Assignment, PA):一个角色可以包含多个权限,一个权限也可以被多个角色引用。这也是多对多关系。
通过这两层映射,最终形成 用户 → 角色 → 权限 的授权链路。当判断用户是否有权执行某操作时,系统只需检查该用户的所有角色中,是否至少有一个角色包含了该操作对应的权限。
数学表达:
设 U = {用户集合},R = {角色集合},P = {权限集合}。
UA ⊆ U × R(用户-角色分配关系)
PA ⊆ R × P(角色-权限分配关系)
用户 u 拥有的权限集合 = { p | ∃ r ∈ R, (u, r) ∈ UA 且 (r, p) ∈ PA }
设 U = {用户集合},R = {角色集合},P = {权限集合}。
UA ⊆ U × R(用户-角色分配关系)
PA ⊆ R × P(角色-权限分配关系)
用户 u 拥有的权限集合 = { p | ∃ r ∈ R, (u, r) ∈ UA 且 (r, p) ∈ PA }
2.3 为什么需要角色这一层?
如果不引入角色,直接让用户与权限关联(即 User-Permission 直连),会出现以下问题:
- 管理爆炸:1000 个用户 × 50 个权限 = 50000 条关联记录,维护成本极高。
- 变更困难:当某个岗位的职责发生变化时,需要逐一修改每个相关用户的权限,极易遗漏。
- 缺乏语义:权限列表无法表达「这个人是什么岗位」,审计和合规检查困难。
引入角色后,上述问题迎刃而解:
- 批量管理:只需维护 10 个角色 × 50 个权限 = 500 条关联,再为 1000 个用户分配角色即可。
- 变更隔离:修改角色的权限集合,所有持有该角色的用户自动生效。
- 语义清晰:角色名称直接反映职责(如「财务审核员」),便于审计和权限梳理。
常见误区:有些团队在初期为了「省事」,直接给每个用户单独配置权限,导致后期权限混乱、无人敢清理。请记住:没有角色层的权限系统,本质上不是 RBAC。
2.4 权限的粒度:操作 + 资源
在 RBAC0 中,权限通常由两个维度组成:
| 维度 | 说明 | 示例值 |
|---|---|---|
| 操作(Action) | 对资源执行的行为 | create, read, update, delete (CRUD) |
| 资源(Resource) | 被操作的对象 | 订单、文章、用户、报表 |
一个完整的权限可以表示为 操作:资源 的组合,例如:
order:create # 创建订单
article:delete # 删除文章
report:view # 查看报表
user:update # 修改用户信息
在数据库设计中,权限表通常包含 action 和 resource 两个字段,或者直接使用字符串标识(如 order.create)。
2.5 数据模型设计(核心表结构)
基于 RBAC0 的三要素和两层关联,数据库至少需要以下 5 张表:
| 表名 | 说明 | 关键字段 |
|---|---|---|
user |
用户表 | id, username, password, status |
role |
角色表 | id, name, description |
permission |
权限表 | id, action, resource, description |
user_role |
用户-角色关联表 | user_id, role_id |
role_permission |
角色-权限关联表 | role_id, permission_id |
建表 SQL 示例(MySQL):
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(64) NOT NULL,
`password` varchar(256) NOT NULL,
`status` tinyint(4) DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `role` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL,
`description` varchar(256) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `permission` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`action` varchar(32) NOT NULL,
`resource` varchar(64) NOT NULL,
`description` varchar(256) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_action_resource` (`action`, `resource`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `user_role` (
`user_id` bigint(20) NOT NULL,
`role_id` bigint(20) NOT NULL,
PRIMARY KEY (`user_id`, `role_id`),
KEY `idx_role_id` (`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `role_permission` (
`role_id` bigint(20) NOT NULL,
`permission_id` bigint(20) NOT NULL,
PRIMARY KEY (`role_id`, `permission_id`),
KEY `idx_permission_id` (`permission_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.6 权限判断逻辑(伪代码)
在运行时,判断用户是否拥有某个权限的典型流程如下:
// 判断用户 user 是否拥有 action:resource 权限
boolean hasPermission(User user, String action, String resource) {
// 1. 查询用户的所有角色
List<Role> roles = userRoleMapper.findRolesByUserId(user.getId());
// 2. 查询这些角色拥有的所有权限
List<Permission> permissions = rolePermissionMapper
.findPermissionsByRoleIds(roles.stream().map(Role::getId).collect(toList()));
// 3. 判断目标权限是否在集合中
return permissions.stream()
.anyMatch(p -> p.getAction().equals(action)
&& p.getResource().equals(resource));
}
性能优化提示:生产环境中不建议每次请求都查数据库。通常的做法是:用户登录后,将其所有权限缓存到 Redis(如 Set 结构),判断时直接查缓存,O(1) 时间复杂度。
2.7 本章小结
- RBAC0 定义了 用户、角色、权限 三个核心实体,以及 UA(用户-角色) 和 PA(角色-权限) 两层多对多关联。
- 角色作为中间层,实现了权限的批量管理、变更隔离和语义化,是 RBAC 灵活性的基石。
- 权限由 操作 + 资源 两个维度构成,数据库设计至少需要 5 张表来支撑完整模型。
- 权限判断的核心逻辑是:用户 → 角色 → 权限 的链路查询。