用户-角色-权限关系模型详解
好了,咱们正式开始聊核心。RBAC 最精髓的地方,就是它把用户和权限解耦了。怎么解耦?中间加了一层——角色。说白了,就是「用户→角色→权限」这么一个三层结构。
我这几年做接口安全,遇到不少团队上来就给用户直接挂权限。一开始项目小还行,等用户量一上来,几百个权限点散落在每个人头上,维护起来简直是一场噩梦。后来用了 RBAC 三层模型,整个世界清静了。
为什么需要这种三层模型?
你想想看,如果没有角色,会发生什么?
- 每个新员工入职,管理员得手动勾选几十个权限。
- 某个岗位的职责变了,你要找到所有属于这个岗位的人,一个一个改。
- 权限分配没有模板,全靠人肉记忆,很容易漏掉或错配。
加入角色这一层后,我们把「谁」和「能做什么」彻底分开。用户只和角色挂钩,角色再和权限挂钩。这样权限的变更只需要修改角色,所有关联的用户自动生效。
核心实体定义
我们先来看看三个实体到底长什么样。我习惯用一句话概括:
- 用户(User):系统的操作者,可以是人,也可以是服务账号。
- 角色(Role):一组权限的集合,代表一个职位或身份。
- 权限(Permission):对某个资源(如 API、菜单、数据)的操作许可。
它们之间的关系是:一个用户可以拥有多个角色,一个角色也可以包含多个用户(多对多);同样地,一个角色可以拥有多个权限,一个权限也可以分配给多个角色(也是多对多)。
关键点:用户和权限之间没有直接关系。所有授权必须通过角色中转。这条规则是 RBAC 的安全底线,千万别打破。
关系映射的两种方式
实现方式上,我见过两种主流做法。第一种是直接为用户分配权限(非 RBAC),第二种就是角色桥接(标准 RBAC)。咱们用表格对比一下:
| 特性 | 直接关联(非 RBAC) | 角色桥接(RBAC) |
|---|---|---|
| 维护成本 | 高,每个用户单独管理 | 低,通过角色批量管理 |
| 扩展性 | 差,用户增长时灾难 | 好,角色作为抽象层 |
| 权限审计 | 困难,需要遍历所有用户 | 简单,只看角色即可 |
| 常见场景 | 极小型系统或临时授权 | 企业级应用、SaaS 平台 |
我个人强烈建议,只要你的系统超过 20 个用户或者 10 个权限点,就应该上 RBAC。别等出了问题再重构,那成本高得多。
多对多关系的数据库实现
数据库里怎么表示这种多对多关系?最经典的做法就是用三张表加两张关联表。嗯,直接上建表 SQL:
-- 用户表
CREATE TABLE `user` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL,
`status` TINYINT DEFAULT 1
);
-- 角色表
CREATE TABLE `role` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL,
`description` VARCHAR(200)
);
-- 权限表
CREATE TABLE `permission` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`code` VARCHAR(100) NOT NULL, -- 如 "user:create"
`name` VARCHAR(50),
`resource` VARCHAR(100) -- 如 "/api/users" POST
);
-- 用户-角色关联表
CREATE TABLE `user_role` (
`user_id` BIGINT NOT NULL,
`role_id` BIGINT NOT NULL,
PRIMARY KEY (`user_id`, `role_id`)
);
-- 角色-权限关联表
CREATE TABLE `role_permission` (
`role_id` BIGINT NOT NULL,
`permission_id` BIGINT NOT NULL,
PRIMARY KEY (`role_id`, `permission_id`)
);
小提示:关联表的主键使用联合主键,可以避免重复记录。如果后续需要记录分配时间或操作人,可以加个 created_at 字段。
查询某个用户拥有的所有权限,只需两次 JOIN:
SELECT p.*
FROM user u
JOIN user_role ur ON u.id = ur.user_id
JOIN role_permission rp ON ur.role_id = rp.role_id
JOIN permission p ON rp.permission_id = p.id
WHERE u.id = ?;
我在项目里踩过的坑
讲真,RBAC 模型看似简单,但实现起来容易出问题。我分享几个亲身经历:
- 角色爆炸:曾经有个项目,每个岗位都创建一个角色,结果角色数量比用户还多。后来我们引入了「角色模板」的概念,用属性组合减少角色数量。
- 权限粒度不一致:有的人把按钮级别的权限也做成资源,有的人只做到菜单。最后统一成「资源+操作」的格式,比如
order:delete。 - 缓存穿透:每次请求都查数据库获取权限,QPS 一高直接打崩。我后来加了本地缓存+Redis 两级,再配合权限变更事件清除缓存。
警告:千万不要在用户和权限之间直接建立关联表!一旦开了这个口子,你的权限模型会迅速退化回原始状态,审计和排查都会变得极其痛苦。
模型的可视化
说了这么多,不如一张图来得直观。下面这张 SVG 图展示了用户、角色、权限之间的三层关系:
图上很清楚了:用户 A、B、C 分别属于不同的角色,而每个角色下挂了不同的权限。注意看,权限「删除文章」同时被管理员角色和编辑者角色拥有——这在多对多关系里非常常见。
好了,这一章的核心就是搞懂这三层模型。理解了这个结构,后面咱们才能聊怎么设计权限校验、怎么做动态鉴权。嗯,先消化一下吧。