权限控制
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;
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
2. 按钮权限
通过指令或函数实现按钮级权限控制。
指令方式:
vue
<template>
<!-- 用户拥有指定权限才显示 -->
<el-button v-hasPerm="['sys:user:add']">新增</el-button>
</template>1
2
3
4
2
3
4
函数方式:
vue
<script setup lang="ts">
import { hasPerm } from "@/utils/auth";
// 在逻辑中判断权限
if (hasPerm(["sys:user:add"])) {
// 执行操作
}
</script>1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
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"],
},
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
按钮权限配置
使用 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>1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
权限指令实现
hasPerm 指令
typescript
// src/directives/permission/index.ts
// 说明:指令会读取 userStore.userInfo.roles/perms,并在无权限时移除节点
export const hasPerm = {
mounted(el, binding) {
// ...
},
};1
2
3
4
5
6
7
2
3
4
5
6
7
指令通过 src/directives/index.ts 全局注册:
typescript
// src/directives/index.ts
export function setupDirective(app) {
app.directive("hasPerm", hasPerm);
}1
2
3
4
2
3
4
hasRole 指令
typescript
// src/directives/permission/index.ts
export const hasRole = {
mounted(el, binding) {
// ...
},
};1
2
3
4
5
6
2
3
4
5
6
需要使用 v-hasRole 时,你可以在 src/directives/index.ts 中自行注册:
typescript
import { hasPerm, hasRole } from "./permission";
export function setupDirective(app) {
app.directive("hasPerm", hasPerm);
app.directive("hasRole", hasRole);
}1
2
3
4
5
6
2
3
4
5
6
权限工具函数
typescript
// src/utils/auth.ts
// hasPerm(value, type?) 既可以校验按钮权限,也可以校验角色
export function hasPerm(
value: string | string[],
type: "button" | "role" = "button"
): boolean {
// ...
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
动态路由与守卫(源码位置)
- [路由守卫]
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 // 用户导出1
2
3
4
5
2
3
4
5
2. 角色命名规范
使用大写字母和下划线:
ADMIN // 管理员
USER // 普通用户
GUEST // 访客1
2
3
2
3
3. 前后端权限一致
- 前端权限标识与后端保持一致
- 前端校验 + 后端校验双重保障
- 敏感操作务必在后端进行权限校验
4. 权限缓存
- 权限信息缓存在 Pinia Store 中
- 避免频繁请求后端获取权限
- Token 过期后清除权限缓存
常见问题
1. 刷新页面后路由消失
原因:动态添加的路由没有持久化
解决:在路由守卫中重新生成路由
2. 权限更新不及时
原因:权限信息缓存未更新
解决:重新登录或清除缓存
3. 按钮权限不生效
原因:
- 权限标识配置错误
- 用户权限未正确获取
- 指令使用不当
解决:
- 检查权限标识是否正确
- 确认用户权限已正确获取
- 确认指令使用方式正确
