后台管理系统之权限设计

8 阅读4分钟

后台管理系统之权限设计(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 方法建议映射为:

MethodAction
GETread
POSTcreate
PUT/PATCHupdate
DELETEdelete

示例:

/api/users + read
/api/users + create

2. Menu 权限(页面级)

通常只需要:

menu:userview

用于控制:

  • 是否显示菜单
  • 是否允许访问页面

3. Button 权限(细粒度 UI)

user:createexecute
user:deleteexecute

用于:

  • 按钮显示/隐藏
  • 前端交互控制

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-…