4、资源树与权限点:资源目录结构设计、权限点(Permission)的定义、资源与权限的绑定

好,我们进入第四章。这一章要聊的是权限模型里最「接地气」的部分——资源树和权限点。

说白了,前面几章我们搭好了用户、角色、组织的架子。但权限最终要落到哪里?落到具体的功能按钮、数据条目、API接口上。这些东西怎么组织、怎么命名、怎么跟权限挂钩,就是本章要解决的问题。

我个人习惯把这一章称为「权限模型的骨架」。骨架搭歪了,后面所有策略都跑偏。

4.1 资源目录结构设计

先问一个问题:你手头有 200 个功能点,怎么把它们排整齐?

我见过不少团队,直接把功能列表平铺开,一个扁平的大数组。结果呢?权限配置页面滚动条拉不到底,运维人员配权限时眼睛都看花了。

正确的做法是——树形结构。

核心原则:资源树应该映射业务的组织方式,而不是代码的模块划分。

举个例子。一个电商后台,资源树可以这样分:

电商后台管理系统
├── 商品管理
│   ├── 商品列表
│   ├── 商品上架
│   ├── 商品编辑
│   └── 商品分类
├── 订单管理
│   ├── 订单列表
│   ├── 订单详情
│   ├── 订单发货
│   └── 退款处理
├── 用户管理
│   ├── 用户列表
│   ├── 用户详情
│   └── 用户封禁
└── 系统设置
    ├── 角色管理
    ├── 操作日志
    └── 系统配置

你看,这个树有三层:模块 → 子模块 → 功能点。实际项目中,我建议最多不超过四层。太深了,配置起来反而麻烦。

我的经验:树形结构设计时,每个节点的「编码」要预留扩展位。比如用 4 位数字编码:0101 代表「商品管理-商品列表」,0102 代表「商品管理-商品上架」。这样中间插入新功能时,编码不会乱。

4.2 权限点(Permission)的定义

资源树搭好了,接下来要给每个叶子节点(也就是具体功能)打上「权限标签」。这个标签,就是权限点。

权限点怎么定义?我有一套固定的命名规范,用了好多年:

字段 说明 示例
权限编码 全局唯一,用于程序判断 goods:list
权限名称 中文描述,用于界面展示 商品列表查看
所属模块 关联到资源树节点 商品管理
权限类型 功能权限 / 数据权限 / 接口权限 功能权限
排序号 控制显示顺序 1

这里有个坑,我踩过。权限编码的命名风格,一定要统一。要么全小写+冒号分隔,要么全小写+点号分隔。别混用。

我曾经在一个项目里看到这样的编码:goods:list、order.detail、User_Edit。三种风格混在一起,后端做权限校验时,解析逻辑写了三套。后来重构时,我花了整整两天才统一完。

我个人推荐用 模块:子模块:操作 的格式。比如:

goods:list          // 商品列表查看
goods:create        // 商品新增
goods:edit          // 商品编辑
goods:delete        // 商品删除
order:ship          // 订单发货
order:refund        // 订单退款
user:ban            // 用户封禁
system:role:assign  // 角色分配

为什么用冒号?因为冒号在大多数编程语言里是合法的标识符字符,而且视觉上比点号更清晰。你想想看,system:role:assign 和 system.role.assign,哪个更容易一眼看出层级?

4.3 资源与权限的绑定

资源树和权限点都定义好了,接下来就是「绑定」——把权限点挂到资源树的节点上。

这里有两种绑定模式,我分别说一下:

4.3.1 显式绑定

每个资源节点直接关联一个或多个权限点。比如「商品列表」这个页面,关联 goods:list 这个权限点。用户点击菜单时,系统检查用户是否有这个权限。

// 资源节点定义示例
{
  "id": "node_001",
  "name": "商品列表",
  "parentId": "module_goods",
  "permissions": ["goods:list", "goods:export"]
}

4.3.2 隐式绑定

有些权限是「继承」来的。比如你有了「商品编辑」的权限,系统默认你也能「查看商品列表」。这种场景下,不需要显式绑定查看权限。

注意:隐式绑定虽然省事,但容易造成权限扩散。我建议只在「查看」类权限上使用,增删改操作必须显式绑定。

实际项目中,我通常采用 混合模式:

  • 菜单/页面级别的资源,用显式绑定,一个节点绑 1-3 个权限点
  • 按钮级别的资源,用隐式绑定,通过父级权限自动派生
  • 数据级别的资源(比如只能看自己部门的订单),单独用数据权限规则处理

下面这张图,展示了资源树、权限点、角色三者之间的关系:

资源树与权限点绑定关系图 资源树 商品管理 商品列表 商品上架 商品编辑 订单管理 订单列表 订单发货 权限点 goods:list goods:create goods:edit goods:delete order:list order:ship 角色 商品管理员 拥有商品所有权限 订单处理员 拥有订单查看/发货 超级管理员 拥有全部权限 说明:资源树的每个叶子节点绑定一个或多个权限点,权限点再分配给角色。角色最终决定用户能访问哪些资源。

4.4 绑定时的常见陷阱

嗯,这里要注意几个容易翻车的地方:

  1. 权限点粒度过粗——一个权限点管了整个模块。结果就是,要么给太多权限,要么给太少。我建议一个权限点只对应一个操作。
  2. 权限点粒度过细——每个按钮都搞一个权限点。配置页面密密麻麻,运维人员直接崩溃。按钮级别的权限,用角色模板去批量处理。
  3. 资源树和权限点不同步——删了某个功能,但权限点还在数据库里躺着。时间久了,权限表里一堆「幽灵权限」。我习惯在发布流程里加一道检查:每次上线前,跑脚本清理无主权限点。

一个小技巧:在数据库里给权限点加一个 status 字段。0=启用,1=禁用,2=废弃。废弃的权限点不删除,只是不再出现在配置界面。这样既干净,又保留了历史数据,万一回滚还能恢复。

好了,这一章的内容就这些。资源树是骨架,权限点是血肉,绑定是连接它们的韧带。这三样东西设计好了,后面的权限继承、角色派生才能站得住脚。