后台管理系统之权限设计(RBAC + 数据权限的工程实践)
在企业级后台系统中,权限设计往往决定了系统的安全性、可扩展性与维护成本。一个好的权限模型,不仅要能控制“能不能访问接口”,还要解决“能看到哪些数据”。
本文结合实际工程经验,系统性梳理一套生产级权限设计方案,涵盖:
- RBAC(基于角色的访问控制)
- 数据权限(DataScope)
- 部门体系(Dept Tree)
- 与 Casbin 的集成策略
一、权限系统的本质
权限系统本质上回答两个问题:
1. 功能是否允许访问?(功能权限)
2. 可以访问哪些数据?(数据权限)
对应两层:
| 层级 | 作用 |
|---|---|
| 接口权限(RBAC) | 控制是否能调用 API |
| 数据权限(DataScope) | 控制能看到哪些数据 |
二、核心模型设计
1. 基础实体关系
User ←→ Role ←→ Permission
数据库表:
users
roles
permissions
user_roles
role_permissions
2. 统一权限模型(关键设计)
type PermissionModel struct {
Name string
Domain PermissionType // menu / api / button / data
Resource string // /api/user 或 menu:user
Action string // read/create/update/delete
}
3. 权限三元组模型
(subject, resource, action)
例如:
(role_admin, /api/user, read)
这与 Casbin 模型天然一致:
sub, obj, act
三、不同 Domain 的职责划分
这是权限设计中最容易混乱的地方。
| Domain | 作用 |
|---|---|
| api | 后端接口权限(核心) |
| menu | 菜单可见性 |
| button | 前端按钮控制 |
| data | 数据范围控制 |
1. API 权限(核心安全)
HTTP 方法建议映射为:
| Method | Action |
|---|---|
| GET | read |
| POST | create |
| PUT/PATCH | update |
| DELETE | delete |
示例:
/api/users + read
/api/users + create
2. Menu 权限(页面级)
通常只需要:
menu:user → view
用于控制:
- 是否显示菜单
- 是否允许访问页面
3. Button 权限(细粒度 UI)
user:create → execute
user:delete → execute
用于:
- 按钮显示/隐藏
- 前端交互控制
4. Data 权限(最复杂)
用于控制:
能看到哪些行数据
四、数据权限设计(DataScope)
1. 权限模型
type DataScope string
const (
DataScopeAll = "all"
DataScopeDept = "dept"
DataScopeSelf = "self"
)
2. 查询过滤实现
func ApplyDataScope(db *gorm.DB, perm *DataPermission) *gorm.DB {
switch perm.Scope {
case DataScopeAll:
return db
case DataScopeDept:
return db.Where("dept_id = ?", perm.DeptID)
case DataScopeSelf:
return db.Where("user_id = ?", perm.UserID)
}
return db
}
3. 业务表设计(关键)
orders
-------
id
user_id -- 创建人
dept_id -- 所属部门(必须)
👉 为什么必须有 dept_id?
- 支持部门级过滤
- 保证历史数据正确
- 避免复杂 JOIN
五、部门体系设计(Dept Tree)
部门是数据权限的核心支撑。
1. 推荐结构(Materialized Path)
type Dept struct {
ID uint
ParentID uint
Path string // "/1/2/3/"
Level int
}
2. Path 字段作用
/1/2/3/
表示从根到当前节点的路径。
3. 子部门查询
WHERE path LIKE '/1/2/%'
👉 优点:
- 无递归
- 可走索引
- 查询极快
4. 为什么不用纯 parent_id?
parent_id 只能表示一层关系
如果查询子树:
❌ 需要递归
❌ 性能差
5. level 字段作用
节点深度(第几层)
用于:
- UI缩进
- 排序
- 避免运行时计算
六、RBAC 与 Casbin 的关系
1. 是否必须使用 Casbin?
结论:
小系统:不需要
中大型系统:建议使用
2. 两种集成方式
方案一:同步模式(casbin_rule)
DB → 同步 → casbin_rule → Enforce()
优点:
- 性能好
- 简单
缺点:
- 数据冗余
- 需要同步机制
方案二:零同步(推荐)
DB → Custom Adapter → Casbin
👉 自定义 Adapter,直接从业务表加载:
p, role_1, /api/user, read
g, user_1, role_1
优点:
- 无冗余
- 强一致
缺点:
- 实现复杂
3. Casbin 与 DataScope 的分工
| 模块 | 作用 |
|---|---|
| Casbin | 是否能访问 API |
| DataScope | 能看到哪些数据 |
👉 注意:
❌ 不要用 Casbin 做数据过滤
❌ 不要把 SQL 写进 Casbin
七、完整请求流程
1. 用户请求 API
2. Casbin 判断是否允许访问
3. 通过 → 进入业务逻辑
4. 注入 DataPermission
5. GORM 自动拼接 WHERE 条件
6. 返回过滤后的数据
八、关键优化点总结
1. 权限表优化
- 添加唯一索引(domain + resource + action)
- 标准化 action(read/create/update/delete)
2. 数据权限
- 避免 most permissive(过于宽松)
- 支持部门树(子部门)
3. 部门设计
- 使用 Path(推荐)
- 保留 ParentID
- 使用 Level 提升性能
4. 架构分层
RBAC(API权限) ≠ DataScope(数据权限)
必须分开设计。
九、设计核心思想(最重要)
一个成熟的权限系统必须做到:
1. 权限结构清晰(resource + action)
2. 控制粒度合理(API / UI / 数据分层)
3. 查询性能可控(避免递归)
4. 易于扩展(支持未来复杂规则)
如果系统继续演进,可以进一步扩展:
- 多租户(tenant_id)
- ABAC(属性权限)
- 权限缓存(Redis)
- 分布式策略同步
Github地址:github.com/pddzl/td27-…