权限控制
讲解前端按钮级权限的两种用法(v-permission / v-role 指令与 hasPerm / hasRole 编程式判断)及其判定规则。
本文是 4.x 的归档文档(冻结于 v4.2.0)。该分支只接收勘误修正, 内容基本不再变动;文中的源码路径、配置项与版本号均以 4.2.x 为准,不建议用于新项目。
新项目请查看 5.x 最新文档。已在 4.2.x 上运行的项目,本文仍是可靠依据。
页面级权限由动态路由控制(没权限的菜单根本不下发,见 路由与页签),本页讲页面内部的按钮级权限。前端提供指令式与编程式两套 API,判定数据都来自 userStore 的 roles 和 permissions,在用户登录后的 getInfo() 里赋值(src/stores/modules/user.ts:143)。
权限码与角色标识
权限码是三段式字符串,与后端 CRUD 控制器自动生成的权限标识一一对应,例如 system:user:create、system:role:delete。角色标识是角色编码字符串,内置超级管理员为 role_admin。
判定规则里有两条通配约定。
- 用户权限集合含
*:*:*时,任何权限校验直接通过(超级管理员); - 用户角色集合含
role_admin时,任何角色校验直接通过。
指令式用法
指令在 src/directives/permission/ 下实现,src/directives/index.ts 全局注册为 v-permission 和 v-role。校验不通过时把元素从 DOM 移除(el.parentNode?.removeChild(el)),不是简单的 display: none。
<template>
<a-button v-permission="['system:user:create']" type="primary" @click="onAdd">
新增
</a-button>
<a-button v-permission="['system:user:export']" @click="onExport">
导出
</a-button>
<a-button v-role="['role_admin']" @click="onInit">
初始化
</a-button>
</template>指令值是字符串数组,数组内任一匹配即通过(OR 语义)。mounted 和 updated 两个钩子都会校验,权限数据异步到达时元素会被正确移除。传空数组或非数组会直接抛错,提示正确的使用格式。
指令的典型位置是工具栏按钮和行内操作链接。
<template #action="{ record }">
<a-space>
<a-link v-permission="['system:user:get']" @click="onDetail(record)">详情</a-link>
<a-link v-permission="['system:user:update']" @click="onUpdate(record)">修改</a-link>
<a-link v-permission="['system:user:delete']" @click="onDelete(record)">删除</a-link>
</a-space>
</template>编程式用法
需要组合判断(如「多个权限有一个就显示入口按钮」)时,用 src/utils/has.ts 导出的工具对象。
<script setup lang="ts">
import has from '@/utils/has'
</script>
<template>
<a-button
v-if="has.hasPermOr(['system:user:resetPwd', 'system:user:updateRole', 'system:user:delete'])"
type="text"
title="更多"
>
<icon-more :size="16" />
</a-button>
</template>完整 API 有六个(src/utils/has.ts:27)。
| 方法 | 语义 |
|---|---|
hasPerm(permission) | 具备指定权限 |
hasPermOr(permissions) | 数组内任一权限具备即通过 |
hasPermAnd(permissions) | 数组内权限全部具备才通过 |
hasRole(role) | 具备指定角色 |
hasRoleOr(roles) | 数组内任一角色具备即通过 |
hasRoleAnd(roles) | 数组内角色全部具备才通过 |
编程式判断返回布尔值,常与 v-if 或 :disabled 配合。与指令的区别在于指令是「移除元素」,编程式可以自由决定降级方式(禁用、隐藏、换文案)。
与后端的配合
前端权限控制只做展示层裁剪,真正的鉴权在后端:CRUD 控制器的 BaseController.preHandle 会按请求方法与路径自动生成权限码并校验(如 POST /system/user 对应 system:user:create)。两边约定同一套权限码,新增 CRUD 页面时照 模块:资源:动作 的格式给按钮加指令即可,不需要额外注册。
权限数据在 Pinia 里持久化到 localStorage(user store 只持久化 roles、permissions、pwdExpiredShow),登出或会话失效时清理。
相关源码
- src/directives/permission/hasPerm.ts:
v-permission指令实现 - src/directives/permission/hasRole.ts:
v-role指令实现 - src/utils/has.ts:编程式权限判断
- src/views/system/user/index.vue:指令与编程式混用的完整页面示例