Skip to content

权限控制

vue3-element-admin 提供了完善的权限控制系统,支持多种权限控制方式。

权限类型

1. 路由权限

通过动态路由实现页面级权限控制。用户登录后,根据其角色权限动态生成可访问的路由。

实现原理

typescript
// src/router/guards/permission.ts
// 关键逻辑:首次进入系统时生成动态路由并注册到 router
if (!permissionStore.isRouteGenerated) {
  const dynamicRoutes = await permissionStore.generateRoutes();
  dynamicRoutes.forEach((route) => router.addRoute(route));
  next({ ...to, replace: true });
  return;
}

2. 按钮权限

通过指令或函数实现按钮级权限控制。

指令方式

vue
<template>
  <!-- 用户拥有指定权限才显示 -->
  <el-button v-hasPerm="['sys:user:add']">新增</el-button>
</template>

函数方式

vue
<script setup lang="ts">
import { hasPerm } from "@/utils/auth";

// 在逻辑中判断权限
if (hasPerm(["sys:user:add"])) {
  // 执行操作
}
</script>

3. 角色权限

基于角色的访问控制(RBAC),用户通过角色获得权限。

权限配置

路由配置

在路由的 meta 中配置权限:

typescript
// 路由的 meta 是菜单渲染与页面能力的重要数据来源
// - perms: 后端返回的菜单/路由权限标识(用于按钮权限等)
// - roles: 角色(如需基于角色进一步控制)
{
  path: "/system/user",
  component: () => import("@/views/system/user/index.vue"),
  name: "User",
  meta: {
    title: "用户管理",
    icon: "user",
    keepAlive: true,
    perms: ["sys:user:list"],
    roles: ["ADMIN"],
  },
}

按钮权限配置

使用 v-hasPerm 指令:

vue
<template>
  <div>
    <!-- 单个权限 -->
    <el-button v-hasPerm="['sys:user:add']">新增</el-button>

    <!-- 需要同时满足多个权限 -->
    <el-button v-hasPerm="['sys:user:delete']">删除</el-button>
  </div>
</template>

权限指令实现

hasPerm 指令

typescript
// src/directives/permission/index.ts
// 说明:指令会读取 userStore.userInfo.roles/perms,并在无权限时移除节点
export const hasPerm = {
  mounted(el, binding) {
    // ...
  },
};

指令通过 src/directives/index.ts 全局注册:

typescript
// src/directives/index.ts
export function setupDirective(app) {
  app.directive("hasPerm", hasPerm);
}

hasRole 指令

typescript
// src/directives/permission/index.ts
export const hasRole = {
  mounted(el, binding) {
    // ...
  },
};

需要使用 v-hasRole 时,你可以在 src/directives/index.ts 中自行注册:

typescript
import { hasPerm, hasRole } from "./permission";

export function setupDirective(app) {
  app.directive("hasPerm", hasPerm);
  app.directive("hasRole", hasRole);
}

权限工具函数

typescript
// src/utils/auth.ts
// hasPerm(value, type?) 既可以校验按钮权限,也可以校验角色
export function hasPerm(
  value: string | string[],
  type: "button" | "role" = "button"
): boolean {
  // ...
}

动态路由与守卫(源码位置)

  • [路由守卫] src/router/guards/permission.ts
  • [动态路由生成] src/store/modules/permission-store.ts

最佳实践

1. 权限标识命名规范

采用 模块:功能:操作 的格式:

sys:user:list    // 用户列表查询
sys:user:add     // 用户新增
sys:user:edit    // 用户编辑
sys:user:delete  // 用户删除
sys:user:export  // 用户导出

2. 角色命名规范

使用大写字母和下划线:

ADMIN           // 管理员
USER            // 普通用户
GUEST           // 访客

3. 前后端权限一致

  • 前端权限标识与后端保持一致
  • 前端校验 + 后端校验双重保障
  • 敏感操作务必在后端进行权限校验

4. 权限缓存

  • 权限信息缓存在 Pinia Store 中
  • 避免频繁请求后端获取权限
  • Token 过期后清除权限缓存

常见问题

1. 刷新页面后路由消失

原因:动态添加的路由没有持久化

解决:在路由守卫中重新生成路由

2. 权限更新不及时

原因:权限信息缓存未更新

解决:重新登录或清除缓存

3. 按钮权限不生效

原因

  1. 权限标识配置错误
  2. 用户权限未正确获取
  3. 指令使用不当

解决

  1. 检查权限标识是否正确
  2. 确认用户权限已正确获取
  3. 确认指令使用方式正确

相关链接

基于 MIT 许可发布