菜单和按钮权限
菜单和按钮权限(功能权限)控制"用户能看到哪些菜单、能点哪些按钮"。菜单与按钮在同一层表(mdc_resource_menu,按钮的 menu_type='50' ),权限码就是表的 code。
1. 配置
在菜单管理页按应用维护菜单树:
- 菜单:
code建议按层级:分隔(如console:organization:user); - 按钮:挂在菜单下,
code以动作结尾(如console:organization:user:add,前端常量在web-console/src/constants/perm/console/**);
2. 授权
角色管理页 →「功能权限」Tab:
- 每个已授权应用渲染一张折叠卡片,卡片内是该应用的菜单/按钮树;
- 可勾选范围由
POST /permission/resourceMenu/treeByRoleId决定——只含同组织性质权限集合角色已授的资源(上限封顶,见角色管理 §2); - 「保存」时调用
POST /permission/role/saveRoleResource; - 回显调用
GET /permission/role/findResourceIdByRoleId,按应用返回每个应用下已授权的菜单id。
勾选菜单/按钮的同时,其关联的接口也随之一并完成授权——接口权限无需单独操作(见接口权限)。
3. 鉴权(前端)
登录后前端拉取两份数据:
| 接口 | 返回 | 用途 |
|---|---|---|
GET /anyone/findUserRouter?appId= | 用户可用的菜单路由树(只含目录/菜单/内链/外链,不含按钮) | 动态注册前端路由,决定菜单可见性 |
GET /anyone/findUserResource?appId= | 用户全部菜单 code + 按钮 code | 按钮显隐鉴权 |
按钮权限两种写法(权限码常量在 src/constants/perm/console/**):
<!-- 模板指令:树节点悬浮按钮等 -->
<Button v-hasAnyPermission="[permCode.userAdd]">新增</Button>
<!--
前端 指令权限控制 代码位于:
packages/effects/access/src/directive.ts
packages/effects/access/src/use-access.ts
-->// 配置式按钮:TableAction / CellOperation
{ auth: permCode.userEdit, label: '编辑', onClick: ... }大小写是否敏感由后端 mdp.ignore.case-sensitive 控制(默认 false,忽略大小写)。
4. 鉴权(后端)
前端隐藏按钮只能预防普通用户,真正的防线在后端:
5. 示例:「用户管理」菜单的菜单和按钮权限
配置(菜单管理页):
组织管理(目录,code=console:organization)
└── 用户管理(菜单,code=console:organization:user,path=/console/organization/user)
├── 新增(按钮,code=console:organization:user:add)
├── 编辑(按钮,code=console:organization:user:edit)
└── 删除(按钮,code=console:organization:user:delete)授权(角色管理页 → 功能权限 Tab):给"人事专员"角色勾选「用户管理」菜单 + user:edit 按钮,不勾 user:add、user:delete。
鉴权效果:
- 该角色用户登录后,路由树中出现「用户管理」菜单;
- 页面上的「新增」「删除」按钮因
v-hasAnyPermission/auth不通过而不渲染,「编辑」按钮可见; - 若用户绕过前端直接调
POST /organization/user/save,该接口关联在user:add按钮上,uri 鉴权拦截(见接口权限)。
6. 排障速查
| 现象 | 排查 |
|---|---|
| 菜单不出现 | 菜单 state → 应用是否授权角色 → 功能权限是否勾选 → 路由树接口返回 |
| 按钮不显示 | 按钮是否勾选授权 → 前端 resourceList 是否含该 code → 权限码拼写/大小写配置 |
| 授权时勾不到 | 同性质权限集合角色是否已授该资源(上限未放开) |