vue的权限管理
# vue的权限管理
# 菜单权限
主要逻辑就是:获取菜单权限列表,动态递归生成菜单。
我们项目中菜单权限列表是由后端返回的,随后将后端返回的权限列表与本地路由表进行对比,拿到所有有权限的路由,然后通过递归生成多级菜单。
在vuex中获取权限列表,并过滤掉没有权限的路由:
<!--代码仅供参考-->
const mutations = {
// 保存所有有权限的路由
SET_ROUTES: (state, data) => {
const aRoutes = getAsyncRoutes();
for (let i = aRoutes.length - 1; i >= 0; i--) {
if (
aRoutes[i].meta &&
!permissionArr.includes(aRoutes[i].meta.title) // permissionArr 权限列表
) {
aRoutes.splice(i, 1); // 删除没有权限的路由
}
}
state.addRoutes = aRoutes;
}
};
// 获取权限列表
const actions = {
authenticateUser({ commit }) {
return new Promise((resolve, reject) => {
authenticateUser()
.then((response) => {
const { data } = response;
commit("SET_ROUTES", data.menuInfos);
resolve();
})
.catch((error) => {
reject(error);
});
});
},
};
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
31
32
33
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
31
32
33
随后在sidebar
中使用递归展示所有层级菜单:
<!--代码仅供参考-->
<!--index.vue-->
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:unique-opened="false"
:active-text-color="variables.menuActiveText"
:collapse-transition="false"
mode="vertical"
>
<sidebar-item
v-for="route in routes"
:key="route.path"
:item="route"
:base-path="route.path"
/>
</el-menu>
<!--sidebarItem.vue-->
<template v-if="!item.hidden">
...
</template>
<el-submenu
v-else
ref="subMenu"
:index="resolvePath(item.path)"
popper-append-to-body
>
<template slot="title">
<item
v-if="item.meta"
:icon="item.meta && item.meta.icon"
:title="item.meta.title"
/>
</template>
<sidebar-item
v-for="child in item.children"
v-show="child.hidden !== true"
:key="child.path"
:is-nest="true"
:item="child"
:base-path="resolvePath(child.path)"
class="nest-menu"
/>
</el-submenu>
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
上面代码是在vue-elelment-admin项目的基础上做了一些修改
# 路由权限
上面的菜单权限虽然做到能看不见菜单,但是我们仍可以通过直接输入url的方式访问到没有权限的页面,这种情况就需要靠路由权限
来阻止。
这里有两个方案:
- 先注册好所有的路由,然后获取有资格访问的路由权限列表,最后直接通过Router.beforeEach来判断,每次跳路由的时候判断是否在权限列表里,在的话就放行,不在就提示权限不够。 优点:简单暴力,不会跳到404页面(因为去的路由能在路由规则里找到) 缺点:由于初始化了所有路由,运行的时候会挂载不必要的路由(?有待考究)
- 先只注册基本路由,然后获取路由权限列表,然后借助
addRoutes()
根据权限列表将有权限的路由动态注册到路由规则上 优缺点与第一种正好相反。
我们项目使用的就是第二种方式,先挂载基本路由,其它路由在用户登陆后根据接口返回的权限列表再通过addRoutes()
挂载有权限的路由。
挂载基本路由:
<!--代码仅供参考-->
// router.js
export const constantRoutes = [
{
path: "/login",
component: () => import("@/views/login/index"),
hidden: true,
},
{
path: "/404",
component: () => import("@/views/404"),
hidden: true,
},
home,
];
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
添加有权限的路由:
<!--代码仅供参考-->
// store/modules/user.js
getInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token) // 登陆后调用获取用户信息接口
.then((response) => {
const { data } = response;
if (!data) {
return reject("Verification failed, please Login again.");
}
commit("SET_USERINFO", data); // 保存当前用户信息,并根据当前用户对应的角色权限获取对应权限的路由/菜单
setTimeout(() => {
router.addRoutes(store.state.permission.addRoutes); // 将有权限的菜单/路由添加到路由上
}, 400);
resolve(data);
})
.catch((error) => {
reject(error);
});
});
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 按钮权限
管理系统页面经常会有新增、删除、编辑、发布等按钮,且不同用户有不同的操作权限,这就需要用按钮权限管理。
同样的,页面中的按钮权限值也是有后端统一返回的。在登陆后,会调用获取权限列表的接口,里面会有各个页面的菜单权限,每个页面下都会定义有按钮权限,使用自定义权限校验指令将按钮的权限值与后端返回的权限值列表进行匹配(需要与后端约定好权限值字段),如果可以匹配到就代表有该按钮的操作权限。
自定义权限校验指令:
<!--代码仅供参考-->
import store from "@/store";
import { Message } from "element-ui";
function checkPermission(el, binding) {
const { value } = binding;
const roles = store.getters && store.getters.roles; // 所有按钮权限值列表
const userId = store.getters && store.getters.userId;
if (value && value instanceof Object) {
const _value = value.roles ? value.roles : value;
const _creator = value.creator ? value.creator : "";
if (_value.length > 0) {
const permissionRoles = _value; // 按钮的权限值
const hasPermission = roles.some((role) => {
return permissionRoles.includes(role); // 判断权限值列表中是否有该按钮权限
});
// 没有该权限值代表没有该按钮的操作权限
if (!hasPermission) {
if (_creator && _creator === userId) return; // 对于编辑删除按钮,用户没有超级权限时,可以编辑自己创建的活动
const cloneEl = el.cloneNode(true);
cloneEl.addEventListener("click", function () {
Message.error("没有该操作权限,请联系系统管理员!");
});
// 替换掉原来绑定事件的节点
el.parentNode && el.parentNode.replaceChild(cloneEl, el);
}
}
} else {
throw new Error(`need roles! Like v-permission="['admin','editor']"`);
}
}
export default {
inserted(el, binding) {
checkPermission(el, binding);
},
update(el, binding) {
checkPermission(el, binding);
},
};
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
给按钮添加权限校验指令:
<!--代码仅供参考-->
<el-button
v-permission="['OP:TABLE_UPDATE']"
class="operation_btn"
size="small"
type="text"
@click="handleEdit(scope.row)"
>
编辑
</el-button>
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
后端返回数据格式:
<!--代码仅供参考-->
// 这是没有编辑权限的数据格式
......
{
"menuParentId": "100-01",
"permId": "100-01-01",
"menuName": "红包页",
"permValue": null,
"menuId": "100-01-01",
"menuType": "menu",
"subPermission": [
{
"menuParentId": "100-01-01",
"permId": "100-01-01-01",
"menuName": "创建",
"permValue": "OP:TABLE_CREATE",
"url": null,
"menuId": "100-01-01-01",
"menuType": "button",
"subPermission": null
},
{
"menuParentId": "100-01-01",
"permId": "100-01-01-03",
"menuName": "删除",
"permValue": "OP:TABLE_DELETE",
"url": null,
"menuId": "100-01-01-03",
"menuType": "button",
"subPermission": null
},
]
}
......
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
31
32
33
34
35
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
31
32
33
34
35
# 参考文献
- https://mp.weixin.qq.com/s/b-D2eH1mLwL_FkaZwjueSw
- https://segmentfault.com/a/1190000020887109
- https://juejin.cn/post/6844903648057622536#heading-6
在GitHub上编辑 (opens new window)
上次更新: 5/27/2022, 4:57:31 PM