RBAC密钥权限设计实战教程 - 第2节
用户-角色-权限:三元组关系,到底是个啥?
讲RBAC,绕不开这个铁三角。说白了,就是用户不能直接拥有权限,中间必须夹着一个角色。
我早年带团队重构一个旧系统时,发现代码里全是硬编码的 if (user.id === ‘admin’) 之类的东西。结果呢?每次加新人,都得改代码。后来全推翻了,用RBAC那一套重写。嗯,世界清净了。
先看数据模型长什么样
不整那些虚的概念,我直接给你看最核心的三张表。它们的关系,就像一把钥匙开一把锁,但中间有个钥匙环——角色。
| 表名 | 核心字段 | 一句话解释 |
|---|---|---|
| 用户表 | user_id, username | 谁在操作?就是你,或者你的账号 |
| 角色表 | role_id, role_name | 什么身份?管理员、编辑、访客都在这 |
| 权限表 | perm_id, perm_code | 能干什么?比如"创建订单"、"删除用户" |
光这三张不够,还得有两张关联表:用户-角色表 和 角色-权限表。这两张表决定了谁是谁,谁能干什么。
核心逻辑:到底怎么查权限?
一个用户登录后,系统怎么知道他有没有资格点那个删除按钮?流程是这样的:
- 拿到当前用户的 user_id
- 通过 用户-角色表 查出他有哪些角色
- 再通过 角色-权限表 查出这些角色对应的权限集合
- 合并去重,得到该用户最终的权限列表
- 在接口里去校验这个列表是否包含目标权限码
我给你看一段我常用的伪代码,清晰得很:
def check_permission(user_id, required_perm_code):
# 1. 查用户的角色列表
roles = get_user_roles(user_id)
# 2. 收集所有角色拥有的权限
perm_set = set()
for role in roles:
perms = get_role_permissions(role.id)
perm_set.update(perms)
# 3. 检查目标权限是否在集合里
return required_perm_code in perm_set
为什么会这样设计?你想想看,如果用户直接持有权限,那是真的一团乱。每个人配一遍?公司来1000个人,配到手断。
权限粒度设计:粗了不行,细了也累
搞定了基本的三元组,下一个问题来了:这个权限到底要切成多细?
我习惯把权限粒度分成两层来聊:功能权限和数据权限。很多人光讲功能权限,忽略了数据权限,结果项目上线后补丁打到吐血。
功能权限:管你能不能点那个按钮
功能权限就是"能不能访问这个页面"或者"能不能执行这个操作"。比如:
user:create– 创建用户user:delete– 删除用户report:export– 导出报表
我建议你用冒号分隔的字符串作为权限码,前面是资源,后面是动作。这样一看就明白,也方便程序按前缀批量匹配。
数据权限:管你能看到哪些数据
这个才是真正的难点。同样是"查看订单"这个按钮,普通销售只能看自己的订单,销售经理能看整个团队的,财务能看到所有部门的——这不光是功能权限能解决的。
数据权限常见的几种玩法:
| 粒度 | 适用场景 | 实现方式 |
|---|---|---|
| 本人数据 | 查看自己的工单 | where user_id = 当前用户 |
| 部门数据 | 查看本部门报表 | where dept_id = 用户所属部门 |
| 全量数据 | 超级管理员 | 不加过滤条件 |
粒度太细?小心"权限爆炸"
有的人喜欢把权限拆到每一个原子操作上,比如"保存草稿"、"提交审核"、"撤回审核"各做成一个权限。结果是角色配置累死人,权限表里几千条记录。
我的经验是:粒度做到"操作级"就够用了。像CRUD这种,一般四个权限就搞定。如果业务要求某个角色只能"编辑自己创建的内容",这个逻辑应该落到代码层面,而不是拆分权限码。
组合策略:怎么搭才不翻车?
实际落地的方案,我推荐这样的组合拳:
- 功能权限:使用角色-权限模型,粒度到页面按钮级别
- 数据权限:使用用户属性 + 部门树 + 自定义规则,不依赖角色
- 特殊场景:比如临时授权,用"用户-权限"直挂表做补充,但严格控制使用
你想想看,如果所有逻辑都堆在角色里,角色本身会变成一个大泥球。拆开之后,每个模块职责清晰,改起来也安心。
我把这三层关系画成了一张图,方便你理解整体结构:
很多新手做完前两步就觉得搞定了,回头发现数据权限没法控制,又返工。我的建议是:一开始就把功能权限和数据权限分开设计,哪怕数据权限先只做最简单的本人/全部两种,也比后加要好十倍。
嗯,这一章的内容就到这。记住,好的权限设计,最终是为了让你少加班。