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
4.1 KiB
ADR-0011: 统一权限资源树(菜单/权限点管理)
Status
Accepted
Context
现有菜单管理(SysMenu + SystemController 的 /api/system/menus/*)是一个初级 CRUD 脚手架,与 PRD 0002-menu-permission-management 的「统一权限资源树」差距巨大:
- 按钮权限码体系缺失:
SysMenu无perms/denyBehavior/apiUrl/status/description字段,按钮节点只是"菜单子节点",无法支撑按钮级 RBAC 鉴权。 - 零层级约束:
saveMenu直接saveOrUpdate,不校验父子类型组合、不锁编辑 type、不查字段必填。 - 删除无防护:
deleteMenu直接removeById,不检查子节点、不级联清理sys_role_menu中的授权引用,留孤儿授权。 - 无全量资源树接口:只有按当前用户角色过滤的可见菜单树(
/menus/tree),管理员无法拿到全量树维护。 - 无图标上传。
- 无权限码并集:
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(ResourceNodeDTO)。 - 用户端(当前用户可见菜单树):保留现有
GET /api/system/menus/tree,登录后渲染侧边栏用,不破坏现有链路。
两者职责分离:管理端给管理员维护全量资源,用户端给登录用户取可见菜单。
3. 层级约束(服务端强制校验)
| 父节点类型 | 允许子节点类型 |
|---|---|
| 根(parentId = null/0) | catalog、menu |
| catalog | menu |
| menu | button |
| button | 无(叶子) |
- 创建/编辑时校验父子类型组合(6 种组合正反例)。
- 编辑时
type不可更改(编辑态锁定)。 - 字段必填:menu 必填
route;button 必填perms(非空)、denyBehavior、apiUrl、status。 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格式不强制校验,依赖管理员保证格式正确——错了该按钮鉴权永远拒绝,联调时暴露。