# 权限节点联动 :::tip 说明 前后端权限通过统一的权限节点字符串联动,后端 `PermissionRequired` 中间件校验权限,前端 `v-perm` 指令和 `TableAction` 的 `auth` 属性控制按钮显示。 ::: ## 权限节点格式 统一格式:`sys:{module}:{action}` ``` sys:position:page —— 分页查询 sys:position:detail —— 查询详情 sys:position:list —— 获取列表 sys:position:add —— 新增 sys:position:update —— 编辑 sys:position:delete —— 删除 sys:position:batchDelete —— 批量删除 sys:position:status —— 设置状态 sys:position:export —— 导出 sys:position:import —— 导入 ``` ## 后端权限校验 View 类继承 `PermissionRequired` 中间件,声明所需权限: ```python # application/example/views.py class ExampleAddView(PermissionRequired, View): permission_required = ("sys:example:add",) @operation_log(title="案例管理-添加记录", log_type=LogType.ADD) def post(self, request): result = services.add_example(request) return result ``` 校验流程: ``` 1. check_login 中间件从 JWT Token 解析用户 ID 2. PermissionRequired 中间件查询用户角色 → 角色菜单 → 权限节点列表 3. 检查 View 要求的权限节点是否在列表中 4. 用户 ID=1(管理员)跳过检查 ``` ## 前端权限控制 ### v-perm 指令 按钮使用 `v-perm` 指令(满足任一权限即可显示): ```vue 添加岗位 删除 ``` ### TableAction auth 属性 操作列通过 `TableAction` 组件的 `auth` 属性控制: ```typescript const actionColumn = { render(record) { return h(TableAction, { style: 'button', actions: [ { label: '编辑', icon: 'Edit', type: 'warning', onClick: handleEdit.bind(null, record), auth: ['sys:position:update'], }, { label: '删除', icon: 'Delete', type: 'danger', onClick: handleDelete.bind(null, record), auth: ['sys:position:delete'], }, ], }); }, }; ``` ### usePermission Hook 在逻辑中检查权限: ```typescript import { usePermission } from '@/hooks/web/usePermission'; const { hasPermission, hasSomePermission } = usePermission(); // v-if 条件渲染 const showExportBtn = computed(() => hasPermission(['sys:position:export'])); ``` ## 菜单配置 权限节点在菜单管理中配置,菜单类型为「按钮」: ``` 菜单管理 → 系统管理 → 岗位管理 ├── 岗位查询(sys:position:page)—— 类型:菜单 ├── 岗位新增(sys:position:add)—— 类型:按钮 ├── 岗位编辑(sys:position:update)—— 类型:按钮 ├── 岗位删除(sys:position:delete)—— 类型:按钮 ├── 岗位批量删除(sys:position:batchDelete)—— 类型:按钮 └── 岗位状态(sys:position:status)—— 类型:按钮 ``` ## 权限数据流 ``` 后端菜单表 → adminMenus API → routerGenerator 生成路由表(含 permission 字段) ↓ userStore.GetInfo() → 获取 permissions 列表 ↓ v-perm 指令 mounted → hasSomePermission(permissions) ↓ 有权限 → 保留元素 / 无权限 → 移除元素 ``` 前端权限列表在登录后通过 `getUserInfo` 接口获取,存储在 user store 的 `permissions` 数组中: ```typescript // src/store/modules/user.ts GetInfo() { return new Promise((resolve, reject) => { getUserInfo().then((res) => { if (res.permissions && res.permissions.length) { this.setPermissions(res.permissions); // permissions 示例:["sys:position:page", "sys:position:add", ...] } this.setUserInfo(res); resolve(res); }).catch(reject); }); }, ``` ## 联调要点 ``` 1. 后端 endpoint 的 permission_required 权限字符串必须与菜单配置的节点一致 2. 前端 v-perm 指令和 TableAction auth 的权限字符串必须与菜单配置的节点一致 3. 权限字符串区分大小写 4. 用户 ID=1(管理员)拥有所有权限(后端跳过检查) 5. 前端权限列表包含 "*:*:*" 通配符时跳过所有检查 ``` ## 常见问题 ### 按钮不显示 ``` 检查步骤: 1. 确认菜单管理中已配置对应的按钮权限节点 2. 确认角色已分配该菜单权限 3. 确认用户已关联该角色 4. 确认前端 v-perm 字符串与菜单节点完全一致(区分大小写) 5. 确认 userStore.permissions 已正确加载 ``` ### 接口 403 ``` 检查步骤: 1. 确认后端 permission_required 字符串与菜单节点一致 2. 确认菜单类型为「按钮」而非「菜单」 3. 确认角色已分配该权限 4. 确认 Token 未过期 ``` :::tip 温馨提示 权限节点的命名建议遵循 `sys:{module}:{action}` 规范,保持前后端一致。新增模块时,先在菜单管理中配置权限节点,再在后端和前端使用。前端 `v-perm` 仅做 UI 层隐藏,真正的权限校验在后端。 ::: ## 总结 前后端权限通过统一的权限节点字符串联动。后端 `PermissionRequired` 中间件做真正的权限校验(用户 ID=1 跳过),前端 `v-perm` 指令和 `TableAction` 的 `auth` 属性做 UI 层隐藏。权限列表在登录时从后端获取,存储在 user store 中。`*:*:*` 通配符表示超级管理员。