Skip to content

权限节点联动

说明

前后端权限通过统一的权限节点字符串联动,后端 @permission_required 校验权限,前端 v-perm 指令和 TableActionauth 属性控制按钮显示。

权限节点格式

统一格式: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 —— 导入

后端权限校验

Endpoint 使用 @permission_required 装饰器:

python
# src/api/v1/endpoints/position.py
@router.post('/add')
@permission_required("sys:position:add")
@check_demo
def add(request: Request, data: PositionForm):
    return position_service.add(request, data)

校验流程:

1. login_required 从 JWT Token 解析用户 ID
2. permission_required 查询用户角色 → 角色菜单 → 权限节点列表
3. 检查 endpoint 要求的权限节点是否在列表中
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. 后端 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 未过期

温馨提示

权限节点的命名建议遵循 sys:{module}:{action} 规范,保持前后端一致。新增模块时,先在菜单管理中配置权限节点,再在后端和前端使用。前端 v-perm 仅做 UI 层隐藏,真正的权限校验在后端。

总结

前后端权限通过统一的权限节点字符串联动。后端 @permission_required 做真正的权限校验(用户 ID=1 跳过),前端 v-perm 指令和 TableActionauth 属性做 UI 层隐藏。权限列表在登录时从后端获取,存储在 user store 中。*:*:* 通配符表示超级管理员。

小蚂蚁云团队 · 提供技术支持