# 实现RBAC菜单按钮权限管理
# 业务背景
现在后台管理系统一般都会涉及到角色权限的分类,目的是为了使用一套系统多个角色,每个角色分配不同的菜单和按钮权限,进而对不同角色实现权限管理
# 原理
# 权限分配
1.建立数据源
- 项目菜单的数据源
- 功能按钮的数据源
- 角色的数据源
- 人员的数据源
2.把菜单和按钮相应的数据源分配给对应的角色
3.创建人员的时候选择对应的角色
4.用户登录之后即可获取对应的权限
# 技术实现
- 动态路由(对菜单权限进行管理)
- v-permission指令(将权限管理的颗粒度达到到按钮的级别)
# 动态路由
# 基础路由
首先肯定是要有一个登录等基础路由
// 页面静态路由(所有权限都一定会有的路由)
// 直接挂载
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
// ... 可能还会有忘记密码,注册等页面路由
{
path: '/404',
component: () => import('@/views/404'),
hidden: true
}
]
const router = createRouter({
history: createWebHashHistory(process.env.BASEURL),// 这里是hash缓存
routes,
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 动态路由
登录成功后接口返回的用户对应能访问的动态路由。
主要通过两个函数实现。router.addRoute()
和 router.removeRoute()
。
// 需要动态加载的路由
export const asyncRoutes = [
...
]
const asyncRouter=router.addRoutes(asyncRoutes) // 动态添加
// asyncRouter() 可以通过调用回调的方式删除路由
//或使用api: router.removeRoute('about') 其中 about 为路由名称
1
2
3
4
5
6
7
2
3
4
5
6
7
最终的路由可能是
[
{
path: '/user',
component: () => import('@/layouts/UserLayout.vue'),
redirect: '/user/login',
children: [
{
path: 'login',
name: 'login',
component: () => import(/* webpackChunkName: "user" */ '@/views/login/Login.vue'),
},
{
path: 'register',
name: 'register',
component: () => import(/* webpackChunkName: "user" */ '@/views/login/ResetPwd.vue'),
}
],
},
{
name: 'rootRouterIndex',
path: '/',
component: () => import('@/layouts/BasicLayout.vue'), // 主菜单
redirect: '/xxx', // 可以在添加动态路由时通过 asyncRoutes[0].path 来添加默认的内容区,
children: [动态路由]
},
{
path: '/404',
component: () => import(/* webpackChunkName: "fail" */ '@/views/exception/404.vue'),
},
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 控制按钮级权限
思路
给每一个按钮唯一标识,获取所有按钮权限进行逐个比对,有这个标识则显示,否则就不显示
示例
<button
v-permission="'base:soft:add'" // 唯一标识为 'base:soft:add'
@click="handleAdd"
>
添加
</button>
1
2
3
4
5
6
2
3
4
5
6
- 实现 v-permission 指令
v-permission 的作用就是动态添加或者移除按钮等元素
// directive.js
export const directives=(app)=>{
const permissions = ["base:soft:add", "base:soft:cut"]
app.directive('permission', (el, binding) => {
const value = binding.value
const isBind=Reflect.has(permissions, "*:*:*")||Reflect.has(permissions, value)
if (!isBind) {
el.parentNode && el.parentNode.removeChild(el)
}
})
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
// mian.js
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// 引入全局指令
import { directive } from './directive/directive'
// 全局注册
// 注意:要放在 const app = createApp(App) 之后
directive(app)
app.mount('#app')
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 遇到的问题
使用addRoute
添加动态路后,router.options
的值是不会更新的,即无法获取添加后的完整的路由表。
- 所以在获取到动态路由后需要将动态路由放到状态管理器中,方便后续
menu
使用。