# 9.7 权限控制 :::tip 概述 前端权限是按钮级 UX 控制:directives/permission.ts 按 getUser 返回的 permissions 数组控制元素显隐;超管通配权限 `*:*:*` 直接放行。后端接口级校验是强制层(403),前端指令只是体验层。 ::: ## 权限数据流 ```text 登录 → GetInfo(/api/index/getUser) → userInfo.permissions(后端 LoadIdentity 的 Perms) 超管 = ["*:*:*"](通配) → store 缓存 → 指令消费 ``` 后端 Perms 来源:role_menu → menu(type=1 且 status=0 且 hide=0)的 permission 去重集合(详见 [5.4 权限管理(RBAC)](../core/rbac))。 ## 指令用法(directives/permission.ts) ```text 按钮级控制(语义示意): 新增 指令逻辑: permissions 含 "*:*:*" → 放行(超管) permissions 含传入权限码 → 放行 否则 → 移除元素(v-if 等效) ``` ## 前后端权限对照(联调要点) ```text 后端 gmeta permission:"sys:user:add" ↔ 前端指令 v-permission="'sys:user:add'" ↔ 菜单权限节点 permission=sys:user:add ↔ 角色已勾选该节点 四处必须一致——不一致的表现: 前端可见按钮但后端 403 → 节点存在但角色未勾选(或身份缓存旧) 前端按钮消失但后端有权限 → permissions 缓存旧 / 指令码拼错 ``` ## 双重校验的定位 | 层 | 位置 | 性质 | |----|------|------| | 前端指令 | v-permission | UX 控制(防误操作、界面简洁)——**可被绕过** | | 后端校验 | Bind 包装层 HasPerm | 强制安全(403 拒绝)——唯一真值 | ::: warning 定位认知 前端权限不是安全边界。绕过前端直接调接口的请求由后端 403 拦截。新增接口**必须**声明 gmeta permission(或评审确认留空=登录即可)。 ::: ## 菜单与按钮的分层 ```text 菜单(type=0):控制"页面能否访问"(路由生成层面) 按钮(type=1 权限节点):控制"页面内操作能否看到"(指令层面) getUser.permissions 下发权限码集合;菜单树不含按钮节点 ``` ## 开发规约 + 页面按钮逐个对齐 gmeta permission 码(复制粘贴防拼错) + 新增权限码流程:gmeta 声明 → 菜单建节点 → 角色勾选 → 指令引用 + 不要发明前端私有权限码(必须与后端节点一致才有意义) + 超管测试:admin 账号应看到全部按钮(`*:*:*` 放行) + 权限变更后重新登录验证(身份缓存 300s;前端 GetInfo 在登录/刷新时拉取)