194 lines
5.8 KiB
Markdown
194 lines
5.8 KiB
Markdown
# 权限节点联动
|
|
|
|
<!-- Copyright (c) 2021-2026 小蚂蚁云团队 版权所有 -->
|
|
|
|
:::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
|
|
<!-- src/views/system/position/index.vue -->
|
|
<a-button type="primary" @click="handleAdd" v-perm="['sys:position:add']">
|
|
添加岗位
|
|
</a-button>
|
|
<a-button type="danger" @click="handleDelete()" :disabled="!selectionData.length"
|
|
v-perm="['sys:position:batchDelete']">
|
|
删除
|
|
</a-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. 后端 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 中。`*:*:*` 通配符表示超级管理员。
|