You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

4.1 KiB

ADR-0011: 统一权限资源树(菜单/权限点管理)

Status

Accepted

Context

现有菜单管理(SysMenu + SystemController/api/system/menus/*)是一个初级 CRUD 脚手架,与 PRD 0002-menu-permission-management 的「统一权限资源树」差距巨大:

  1. 按钮权限码体系缺失SysMenuperms/denyBehavior/apiUrl/status/description 字段,按钮节点只是"菜单子节点",无法支撑按钮级 RBAC 鉴权。
  2. 零层级约束saveMenu 直接 saveOrUpdate,不校验父子类型组合、不锁编辑 type、不查字段必填。
  3. 删除无防护deleteMenu 直接 removeById,不检查子节点、不级联清理 sys_role_menu 中的授权引用,留孤儿授权。
  4. 无全量资源树接口:只有按当前用户角色过滤的可见菜单树(/menus/tree),管理员无法拿到全量树维护。
  5. 无图标上传
  6. 无权限码并集PermissionServiceImpl 只建菜单树,JWT 只填 roleCode,按钮级 hasAuthority 不可用。

Decision

1. 数据模型:同表补字段 + 枚举映射

SysMenu 同表新增 5 个字段(按 type 区分适用字段,空值表示不适用):

字段 类型 适用节点
description varchar(100) 全部
perms varchar(100) button
denyBehavior varchar(16) button(hide/disable)
status varchar(16) button(enabled/disabled,缺省 enabled)
apiUrl varchar(200) button

DB menuType 保持 tinyint(1/2/3)不变,兼容现有数据;代码层引入 MenuType 枚举(CATALOG(1)/MENU(2)/BUTTON(3))映射 PRD 的 catalog/menu/button。

2. API 路径:新建 /api/resources + 保留 /menus/tree

  • 管理端(全量资源树 CRUD + 图标上传):新建 ResourceController,路径 /api/resources,入参 JSON body(ResourceNode DTO)。
  • 用户端(当前用户可见菜单树):保留现有 GET /api/system/menus/tree,登录后渲染侧边栏用,不破坏现有链路。

两者职责分离:管理端给管理员维护全量资源,用户端给登录用户取可见菜单。

3. 层级约束(服务端强制校验)

父节点类型 允许子节点类型
根(parentId = null/0) catalog、menu
catalog menu
menu button
button 无(叶子)
  • 创建/编辑时校验父子类型组合(6 种组合正反例)。
  • 编辑时 type 不可更改(编辑态锁定)。
  • 字段必填:menu 必填 route;button 必填 perms(非空)、denyBehaviorapiUrlstatus
  • perms 格式不强制校验(只校验非空)——格式错只影响该按钮自己的鉴权匹配,不污染其他权限码。

4. 删除防护 + 级联清理

  • 删除前检查子节点:有子节点则拒绝。
  • 删除时级联清理 sys_role_menu 中对该节点的授权引用。

5. 权限码并集注入(按钮级鉴权)

PermissionServiceImpl.initDataScopeContext(每请求算数据范围时)顺带算权限码并集:

  • 查用户所有角色 → 查角色授权的菜单 → 过滤 type=button 且 status=enabled → 收集 perms 字符串。
  • roleCode 一起填 Spring Security authority,使 @PreAuthorize("hasAuthority('crm:xxx:yyy')") 生效。
  • 停用的权限点(status=disabled)不进并集——全局断路,无需逐角色取消授权。

6. 图标上传:复用现有 FileApi

POST /api/resources/icon/upload(multipart)调 crm-file 的 FileApi 上传到 MinIO,返回 URL 存入节点 icon 字段。校验 png/jpg/gif/svg/webp + ≤500KB。

Consequences

  • 现有 sys_menu 表需加 5 列(ddl-auto: update 自动加)。
  • 现有 /api/system/menus/save/delete 保留向后兼容(或逐步迁移到 /api/resources),不破坏前端现有调用。
  • 按钮级鉴权启用后,业务接口可逐步从 hasRole 迁移到 hasAuthority('crm:xxx:yyy'),但迁移非阻断性,可渐进。
  • perms 格式不强制校验,依赖管理员保证格式正确——错了该按钮鉴权永远拒绝,联调时暴露。