装完以后:菜单与权限
本文是 1.x 的归档文档(冻结于 1.x)。该分支只接收勘误修正, 内容基本不再变动;文中的源码路径、配置项与版本号均以 1.x 为准,不建议用于新项目。
新项目请查看 5.x 最新文档。已在 1.x 上运行的项目,本文仍是可靠依据。
装一个应用只做两件事:把菜单行建出来,再把它们授权给一个角色。 "只授给一个角色"是这一页最需要记住的一点 —— 别的角色不会自动拥有它。
权限标识本身写在应用清单里(app.json 的 menus[].perms),开发侧的口径见
菜单与权限;菜单归属的机制见 菜单归属与保护。
安装只授给管理员角色
授权的实现是 AppMenuGrantServiceImpl:
| 顺序 | 找谁当被授权的角色 | 找不到时 |
|---|---|---|
| 1 | 本租户的 admin 角色(TENANT_ADMIN_ROLE_KEY) | 往下走 |
| 2 | 本租户的 superadmin 角色(SUPER_ADMIN_ROLE_KEY),并打一条 INFO 日志说明回退了 | 往下走 |
| 3 | —— | 打 WARN:「租户[租户号]既无 admin 也无 superadmin 角色,应用菜单将无法授权」 |
几个直接后果:
- 默认租户(
000000)通常没有admin角色,所以在那里的实际效果是"授权给了 superadmin" —— 这就是"装完只有超级管理员看得见"的来源,不是权限算错,而是回退链走到了第二档; - 该租户两个角色都没有时,菜单行照样建出来,但一个角色都没授权。超级管理员因为走的是
"取全部菜单树"那条路,看起来仍然正常,问题要等真实租户用户登录才会暴露 ——
所以那条
WARN值得看一眼; - 授权是补差集(幂等):已存在的
sys_role_menu关系不重复插。安装与升级都会跑一次, 「启用」也会按该版本的菜单再补一次。
别的角色要手工授权
应用清单(app.json)只能表达菜单树,表达不了"哪个角色该有它" —— 授权不在清单能表达的范围内。
所以除了上面那一个角色,其余角色一律要手工加:
系统管理 → 角色管理 → 选中角色 → 分配菜单 → 勾上这个应用的菜单 → 保存
之后用户重新登录(或刷新页面重建菜单树)就能看到入口。
这也是"搬一个宿主模块成应用"时最容易漏的一步:原设计的可见范围是"谁有底层数据权限谁就能看到页面", 而安装只补一个角色,剩下的要配一条授权 SQL 或手工勾。相关经验见 从宿主模块抽成应用(SOP)。
菜单同步是按 menu_key 幂等 upsert 的,同名菜单复用既有 menu_id。
所以升级之后角色已经勾过的菜单还在,不需要重新分配。
卸载时怎么回收
两种卸载模式回收的范围不同,这是刻意的:
| 模式 | 回收范围 | 实现 |
|---|---|---|
| 保留数据(默认) | 只回收本租户管理员角色对这些菜单的授权 | revokeMenusFromTenantAdmin |
| 彻底删除 | 回收所有角色对这些菜单的授权,然后删菜单行 | revokeMenusFromAllRoles |
保留数据那条只动一个角色,是为了"以后重装即恢复";彻底删除要连菜单行一起删, 留着授权会变成指向不存在菜单的孤儿记录。
停用不是撤授权
停用后菜单入口会消失,但消失的原因是菜单过滤器按安装状态隐藏,不是授权被回收。 所以重新启用立刻恢复,管理员此前的手工分配也不会被破坏。
想让人彻底看不到入口,用「停用」,不要去删菜单。
菜单归属保护:哪些能改,哪些不能改
应用装的菜单是 sys_menu 里的普通行,所以它们会被归属识别出来(归属记录在应用市场自己的映射表里),
在服务端拦掉"改了就会坏"的操作:
| 操作 | 允许? | 理由 |
|---|---|---|
改 path / component / perms / menuType / parentId | ❌ 拒绝 | 与插件代码、前端路由、权限标识绑死,改了下一次升级还会被清单覆盖回去 |
| 改名称 / 图标 / 排序 / 显隐 | ✅ 放行 | 纯展示属性,管理员按习惯调没有风险 |
| 删除 | ❌ 拒绝 | 删了只能靠重装 / 升级恢复。想隐藏请「停用」 |
三个容易搞错的点:
- 保护在服务端,不在页面。 菜单管理页会禁用应用菜单的删除按钮、并打上应用名标签,
但接口是同权限可调的(其它客户端、直接 curl)。真正的收口点是菜单服务的
updateMenu/deleteMenuById,页面禁用只是顺手的提示。 - 框架自带的检查会先拦住大多数删除。「存在子菜单,不允许删除」与「菜单已分配,不允许删除」 都发生在进入归属检查之前,所以归属检查是最后一道,专治"菜单已经成了孤儿"这种情况。 反过来,实测时得先摘掉角色分配才能触达它。
- 归属连后代一起算。 按钮(
menu_type为F,如gallery:image:query)历史上来自平台初始化 SQL、 不在归属映射里,也会被一起保护 —— 否则管理员能随手删掉应用按钮,恢复只能靠重装。
相关阅读
- 装完要刷新菜单才能看到 —— 菜单没出现的三种原因
- 多租户下的应用 —— 菜单行共享、可见性与授权按租户
- 菜单归属与保护 —— 归属从哪来、契约放在哪
- 菜单与权限 —— 开发侧:把菜单与按钮写进清单