ContiNew
前端开发

权限控制

讲解前端按钮级权限的两种用法(v-permission / v-role 指令与 hasPerm / hasRole 编程式判断)及其判定规则。

页面级权限由动态路由控制(没权限的菜单根本不下发,见 路由与页签),本页讲页面内部的按钮级权限。前端提供指令式与编程式两套 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),登出或会话失效时清理。

相关源码

目录