192 lines
5.7 KiB
Markdown
192 lines
5.7 KiB
Markdown
# 权限节点联动
|
|
|
|
:::tip 说明
|
|
前后端权限通过统一的权限节点字符串联动,后端 `@check_permission` 校验权限,前端 `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 —— 导入
|
|
```
|
|
|
|
## 后端权限校验
|
|
|
|
Router 使用 `@check_permission` 装饰器:
|
|
|
|
```python
|
|
# src/modules/system/position/router.py
|
|
@position_blue.route('/add', methods=['POST'])
|
|
@check_permission('sys:position:add')
|
|
def add():
|
|
from src.modules.system.position.service import add_position
|
|
from src.modules.system.position.schema import PositionForm
|
|
data = parse_request_body(request, PositionForm)
|
|
return add_position(data)
|
|
```
|
|
|
|
校验流程:
|
|
|
|
```
|
|
1. login_required 从 JWT Token 解析用户 ID
|
|
2. check_permission 查询用户角色 → 角色菜单 → 权限节点列表
|
|
3. 检查 router 要求的权限节点是否在列表中
|
|
4. 用户 ID=1(管理员)跳过检查
|
|
```
|
|
|
|
## 前端权限控制
|
|
|
|
### v-perm 指令
|
|
|
|
按钮使用 `v-perm` 指令(满足任一权限即可显示):
|
|
|
|
```vue
|
|
<!-- src/views/system/position/index.vue -->
|
|
<el-button type="primary" @click="handleAdd" v-perm="['sys:position:add']">
|
|
添加岗位
|
|
</el-button>
|
|
<el-button type="danger" @click="handleDelete()" :disabled="!selectionData.length"
|
|
v-perm="['sys:position:batchDelete']">
|
|
删除
|
|
</el-button>
|
|
```
|
|
|
|
### 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. 后端 router 的 check_permission 权限字符串必须与菜单配置的节点一致
|
|
2. 前端 v-perm 指令和 TableAction auth 的权限字符串必须与菜单配置的节点一致
|
|
3. 权限字符串区分大小写
|
|
4. 用户 ID=1(管理员)拥有所有权限(后端跳过检查)
|
|
5. 前端权限列表包含 "*:*:*" 通配符时跳过所有检查
|
|
```
|
|
|
|
## 常见问题
|
|
|
|
### 按钮不显示
|
|
|
|
```
|
|
检查步骤:
|
|
1. 确认菜单管理中已配置对应的按钮权限节点
|
|
2. 确认角色已分配该菜单权限
|
|
3. 确认用户已关联该角色
|
|
4. 确认前端 v-perm 字符串与菜单节点完全一致(区分大小写)
|
|
5. 确认 userStore.permissions 已正确加载
|
|
```
|
|
|
|
### 接口 403
|
|
|
|
```
|
|
检查步骤:
|
|
1. 确认后端 check_permission 字符串与菜单节点一致
|
|
2. 确认菜单类型为「按钮」而非「菜单」
|
|
3. 确认角色已分配该权限
|
|
4. 确认 Token 未过期
|
|
```
|
|
|
|
:::tip 温馨提示
|
|
权限节点的命名建议遵循 `sys:{module}:{action}` 规范,保持前后端一致。新增模块时,先在菜单管理中配置权限节点,再在后端和前端使用。前端 `v-perm` 仅做 UI 层隐藏,真正的权限校验在后端。
|
|
:::
|
|
|
|
## 总结
|
|
|
|
前后端权限通过统一的权限节点字符串联动。后端 `@check_permission` 做真正的权限校验(用户 ID=1 跳过),前端 `v-perm` 指令和 `TableAction` 的 `auth` 属性做 UI 层隐藏。权限列表在登录时从后端获取,存储在 user store 中。`*:*:*` 通配符表示超级管理员。
|