第3章 角色模型入门:角色、权限与用户的关系
3.1 为什么需要角色?
你想想看,一个系统里有几百个用户,每个人要读不同的文件、执行不同的操作。一个个去配权限?那简直是灾难。
我第一次接手权限模块时,天真地给每个用户都单独设了一堆权限。结果第二天就有人喊着“我明明能看见这个项目为什么点不了?”查了半天,原来是漏了一条写权限。嗯,后来我就学乖了——直接上角色模型,一劳永逸。
核心思路:把权限打包成“角色”,再把角色分配给用户。用户不再直接和权限打交道,中间多了个管理层。
3.2 核心定义
咱们得先把三个概念理清楚。
- 角色(Role):一组权限的集合,代表一个岗位或职责。比如“管理员”、“编辑”、“审计员”。
- 权限(Permission):对某个资源执行某类操作的许可。例如“读取订单表”、“删除用户”。
- 用户(User):系统里的一个身份实体,可以是一个人、一个服务账号,甚至是一个设备。
这三者的关系可以用一句话概括:用户通过角色获得权限。说白了,用户不直接跟权限挂钩,而是跟角色挂钩。
| 概念 | 含义 | 例子 |
|---|---|---|
| 角色 | 权限的容器 | “内容编辑”角色拥有“创建文章、修改文章、上传图片”权限 |
| 权限 | 一个具体的授权 | “删除评论”权限(资源:评论,操作:delete) |
| 用户 | 系统使用者 | 张三(用户ID: 1001)被赋予“编辑”角色 |
3.3 三者关系
关系图其实不复杂:用户 → 角色 → 权限。而且这种关系通常都是多对多的——一个用户可以拥有多个角色,一个角色也可以被分配给多个用户。权限也一样,一个角色可以有很多权限,一个权限也可能出现在多个角色里。
个人经验:我习惯在数据库里用三张关联表来实现:用户表、角色表、权限表,再加上 user_role 和 role_permission 两张关联表。建表的时候,别忘了加上时间戳和操作人字段,后面审计时能省不少事。
注意:我曾遇到过团队直接把用户ID写到权限表里,说“先跑通再说”。等用户量一上来,改一次权限要遍历所有用户记录,性能直接崩了。角色模型的好处就是——改角色的权限,所有关联用户自动生效。所以别偷懒,一点要建角色层。
3.4 实际案例:文件管理系统
拿一个后端文件管理系统举个例。假设有三种角色:
- 访客:只能浏览公开文件目录。
- 编辑:可以上传、修改、删除自己的文件,还能查看所有共享文件。
- 管理员:全部权限,包括删除他人文件、修改系统配置。
那么对应的权限表大致是这样:
| 权限代码 | 说明 | 访客 | 编辑 | 管理员 |
|---|---|---|---|---|
| file:list | 查看文件列表 | ✓ | ✓ | ✓ |
| file:read | 读取文件内容 | 仅公开 | ✓ | ✓ |
| file:upload | 上传文件 | - | ✓ | ✓ |
| file:delete | 删除文件 | - | 仅自己 | ✓ |
| system:config | 修改系统配置 | - | - | ✓ |
你看,每个角色对应一组权限,用户只要在登录时查一下自己的角色列表,就能立刻知道自己能干什么。
3.5 避坑指南
踩过的坑多了,自然就懂。下面几个点一定要记住:
- 千万别直接给用户赋权。我曾经为了图省事,写了一个过时的管理后台,允许给用户直接勾选权限。结果上线一个月,权限管理直接失控,根本没法批量调整。后来全部重构为角色模型。
- 角色继承要谨慎。很多框架支持角色继承(例如“超级管理员继承普通管理员”),听起来很美,但一旦层级超过两层,查权限来源特别痛苦。我建议你尽量用扁平角色,组合多个角色来满足需求。
- 权限名称要规范。我之前的项目里权限代码写得非常随意:“read_file”、“canRead”、“读取文件”混着用。后来做审计报表时,花了三天才理清实际对应关系。推荐用“资源:操作”格式,比如
order:read、user:delete。
// 一个简单的权限校验伪代码(后端)
function hasPermission(user, requiredPermission):
roles = getUserRoles(user.id) // 获取用户的所有角色
for each role in roles:
permissions = getRolePermissions(role.id) // 获取角色的所有权限
if requiredPermission in permissions:
return true
return false
注意,上面的伪代码里我没有做缓存,实际生产环境一定要把角色-权限关系缓存到Redis里,否则每次请求都查数据库,压力扛不住。
3.6 小结
角色模型的核心就是解耦。用户和权限不再直接绑定,角色作为中间层,让权限管理变得灵活、可扩展。我记得早年刚做权限系统时,总想着“少一层不就少点代码吗”?后来才明白,那层抽象省下的不是代码,是未来几个月的维护时间。