一、为什么需要权限模型
任何多用户系统都要回答两个问题:
- 谁能访问(Authentication → 身份认证)
- 能做什么(Authorization → 授权)
权限模型回答的就是第二个问题。一套设计得当的权限模型能:
- 让权限表达清晰、容易审计
- 让新功能接入权限成本低
- 让安全审计和合规检查有迹可循
二、ACL:访问控制列表
2.1 核心思想
每个资源绑定一份”谁能访问”的清单。
1 | 文件: 2026年财报.pdf |
2.2 数据结构
最简单的 ACL 就是「资源 × 用户 × 操作」三元的集合:
1 | CREATE TABLE acl ( |
2.3 优缺点
优点
- 直观,贴近现实世界的「给谁开门」心智
- 细粒度,可以控制到具体资源实例
- 实现简单,三张表就能跑
缺点
- 用户和资源多了之后清单爆炸:1000 用户 × 10000 资源 × 3 操作 = 3000 万条记录
- 修改权限要逐个资源改,不能批量
- 审计时难以回答”这用户到底能干嘛”
2.4 典型场景
- 操作系统文件权限(Linux
chmod) - 云存储 Bucket Policy(AWS S3)
- 共享链接:临时给某用户开某个文件的访问权
ACL 在复杂业务里往往作为补充(比如给单个文档额外授权某人),而不是主力。
三、RBAC:基于角色的访问控制
3.1 核心思想
在「用户」和「权限」之间加一层角色作为中介。
1 | 用户 ──角色──> 权限 |
权限不再直接挂在用户身上,而是先挂在角色上,再把角色赋给用户。
3.2 五张基础表
1 | -- 用户 |
权限判定伪代码:
1 | def has_permission(user, perm_code): |
3.3 RBAC 三个变体
RBAC0 — 基础模型
就是上面那张图,用户-角色-权限三层多对多关系。95% 的后台系统停在 RBAC0 就够用。
RBAC1 — 角色继承
角色之间有父子关系,子角色自动继承父角色的所有权限。
1 | 超级管理员 |
数据上多加一张 role_parent(role_id, parent_id)。
1 | -- 内容编辑自动继承管理员的"用户列表"权限 |
Django 的
auth.Group没有原生角色继承,但django-guardian或自研一层都能实现。
RBAC2 — 角色约束
在用户-角色关系上加限制:
| 约束类型 | 含义 | 业务例子 |
|---|---|---|
| 互斥角色 | 一个用户不能同时拥有 A 和 B | 出纳 vs 会计(防一人既管钱又管账) |
| 基数约束 | 一个角色最多 N 个人 | 「CEO」只能有 1 个 |
| 先决条件 | 想拿 B 必须先有 A | 「副总经理」必须先是「部门经理」 |
数据库实现通常在 user_roles 上加触发器或应用层校验。
3.4 优缺点
优点
- 用户-权限关系被拆成用户-角色 + 角色-权限,扩展性好
- 调整一类人的权限 = 改一个角色,不用动每个用户
- 角色是天然的”权限标签”,审计时一眼看清
缺点
- 角色数量容易膨胀(几百个角色时管理成本变高)
- 默认是粗粒度,不带”资源实例”维度(不能表达”只能改自己创建的订单”)
四、典型业务场景的权限设计
4.1 普通管理后台(OA / CMS)
最简模型:RBAC0,5 张表跑通。
菜单 / 按钮级权限:
实际后台经常要控制”按钮显隐”,比如普通用户看不到”删除”按钮。常见做法:
1 | -- 权限 code 用模块:动作命名 |
前端用 v-if="hasPerm('user:delete')" 控制按钮渲染,后端在中间件里再做一次校验(前端不可信)。
推荐实现:
- Python:
Django+django.contrib.auth内置Permission+Group - Node:
casl/accesscontrol库
4.2 CRM 系统
CRM 比普通后台多一层:数据归属。
「销售 A 只能看自己跟进的客户」——这是 RBAC 解决不了的(需要数据行级过滤)。
混合方案:RBAC(控制功能)+ 数据归属(控制行)
1 | # Django ORM 配合 manager 的标准做法 |
关键点:
- 功能权限用 RBAC(能不能看客户列表)
- 数据权限走 ORM 层(能看到哪些客户)
- 业界叫法不一:阿里叫「数据权限」,Salesforce 叫「Record-Level Security」
4.3 多租户 SaaS
多租户场景下,权限模型必须隔离两件事:
- 租户间:A 公司的管理员绝对看不到 B 公司的数据
- 租户内:A 公司内部还是要做 RBAC(普通员工 vs 管理员)
三种数据隔离方案对比
| 方案 | 共享库共享 schema | 共享库独立 schema | 独立库 |
|---|---|---|---|
| 隔离强度 | 弱(靠 tenant_id 过滤) | 中 | 强 |
| 成本 | 低 | 中 | 高 |
| 迁移难度 | 低 | 中 | 高 |
| 适用规模 | 中小 SaaS(< 1000 租户) | 中型 SaaS | 大客户 / 金融 |
中小 SaaS 最常见:共享库 + tenant_id + 中间件过滤。
1 | # 中间件根据 JWT 自动注入 tenant_id 过滤 |
超级管理员的”上帝视角”
平台方(运营团队)有时需要跨租户排查问题。设计模式:
- 给平台账号一个特殊角色(如
super_admin),绕过租户过滤 - 所有写操作记录审计日志(”谁在什么时间看了哪家公司的什么数据”)
- 数据脱敏(手机号中间四位 **)
RBAC + 租户的完整结构
1 | 用户表 |
实现上推荐:
- django-tenants:基于 PostgreSQL schema 隔离,Django 生态最成熟
- Apache ShardingSphere:Java 生态的数据库代理层方案
- Casbin multi-tenancy 模式:策略文件按租户分目录
五、常见踩坑
5.1 权限只在前端校验
前端按钮隐藏是给用户体验,真正的权限校验必须在后端。前端代码用户改一下 devtools 就能绕过。
5.2 权限模型一开始设计得太复杂
上来就搞 RBAC1 + RBAC2 + 数据权限 + 字段级权限,结果第一个版本上线就拖了三个月。
建议:MVP 阶段只做 RBAC0 + 菜单权限,后续按业务真实需求再扩展。
5.3 权限数据没做缓存
每次请求都查 5 张表,QPS 一高就崩。
1 | # 用 Redis 缓存用户的权限码列表,TTL 5 分钟 |
角色调整时清缓存(或用消息队列广播失效)。
5.4 角色命名混乱
admin / super_admin / system_admin / manager —— 4 个月后没人记得清区别。
约定:
- 用「业务动作」而不是「职位」命名:
order_manager比manager更准确 - 维护一份权限字典表(
permissions.code→ 中文说明),所有模块共用
六、选型速查表
| 业务场景 | 推荐模型 | 推荐引擎 |
|---|---|---|
| 普通后台(OA/CMS) | RBAC0 | Django auth / Node casl |
| CRM | RBAC + 数据权限行级过滤 | Django + 自定义 manager |
| 多租户 SaaS | RBAC + tenant_id 隔离 | django-tenants / ShardingSphere |
| 文档协作 / 社交 | ReBAC | OpenFGA / Ory Keto |
| 强合规要求 | RBAC + ABAC 混合 | Casbin / OPA |
| 文件系统 / 简单 API | ACL | 直接用 ACL 表 |
七、参考资料
- NIST RBAC 标准(INCITS 359-2012)
- Django 官方文档 - Permissions and authorization
- Apache Casbin 多模型支持
- OpenFGA(Google Zanzibar 开源实现)
总结:
- ACL 适合”我给某个资源临时开个小门”的场景;
- RBAC 是后台系统的默认选择,RBAC0 够用就别上 RBAC1/2;
- CRM = RBAC + 数据行级过滤;
- 多租户 = 租户隔离 + 租户内 RBAC,权限校验必须在后端,缓存要跟上。