权限控制
你需要让「普通用户」看不到「管理员」按钮?本页介绍三种粒度的权限控制方案,从页面级到按钮级,按需选用。
权限体系概览
权限系统由三层组成:
┌─────────────────────────────────────────┐
│ 动态路由(页面级) │
│ → 用户能看到哪些页面 │
├─────────────────────────────────────────┤
│ 角色 RBAC(数据级) │
│ → 用户能看到哪些数据 │
├─────────────────────────────────────────┤
│ v-hasPerm(按钮级) │
│ → 用户能看到哪些按钮 │
└─────────────────────────────────────────┘1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
三层独立工作,可组合使用。
路由权限(页面级)
用户登录后,根据角色权限动态生成可访问的路由。
实现原理
路由守卫首次进入时生成动态路由:
typescript
// src/router/guards/permission.ts
if (!permissionStore.isRouteGenerated) {
const dynamicRoutes = await permissionStore.generateRoutes()
dynamicRoutes.forEach((route) => router.addRoute(route))
next({ ...to, replace: true })
}1
2
3
4
5
6
2
3
4
5
6
路由配置
在路由 meta 中声明权限标识:
typescript
{
path: "/system/user",
component: () => import("@/views/system/user/index.vue"),
meta: {
title: "用户管理",
icon: "user",
perms: ["sys:user:list"], // 权限标识
roles: ["ADMIN"], // 角色限制
},
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
按钮权限
指令方式
使用 v-hasPerm 指令控制按钮显隐:
vue
<template>
<!-- 单个权限 -->
<el-button v-hasPerm="['sys:user:add']">新增</el-button>
<!-- 多个权限(满足其一即可) -->
<el-button v-hasPerm="['sys:user:edit', 'sys:user:update']">编辑</el-button>
</template>1
2
3
4
5
6
7
2
3
4
5
6
7
函数方式
在逻辑中判断权限:
typescript
import { hasPerm } from "@/utils/auth"
if (hasPerm(["sys:user:add"])) {
// 有权限时执行
}1
2
3
4
5
2
3
4
5
指令实现
typescript
// src/directives/permission/index.ts
export const hasPerm = {
mounted(el, binding) {
const userStore = useUserStore()
const perms = userStore.userInfo?.perms || []
const requiredPerms = binding.value
const hasPermission = requiredPerms.some(p => perms.includes(p))
if (!hasPermission) {
el.parentNode?.removeChild(el)
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
权限标识规范
采用 模块:功能:操作 格式:
| 格式 | 示例 |
|---|---|
模块:功能:操作 | sys:user:add |
| 常见操作 | list, add, edit, delete, export |
常见问题
刷新后路由消失?
动态路由在内存中,刷新后需重新生成。路由守卫已处理此场景。
按钮权限不生效?
- 检查权限标识是否与后端一致
- 确认用户已正确获取权限列表
- 打开控制台查看
userStore.userInfo.perms
权限更新不及时?
重新登录,或清除 Pinia 缓存:
typescript
const userStore = useUserStore()
userStore.resetUserInfo()1
2
2
源码位置
- 路由守卫:
src/router/guards/permission.ts - 动态路由生成:
src/store/modules/permission-store.ts - 权限指令:
src/directives/permission/index.ts - 权限函数:
src/utils/auth.ts
