权限
权限
项目中集成了三种权限处理方式:
- 通过用户角色来过滤菜单(前端方式控制),菜单和路由分开配置
- 通过用户角色来过滤菜单(前端方式控制),菜单由路由配置自动生成
- 通过后台来动态生成路由表(后台方式控制)
前端角色权限
实现原理: 在前端固定写死路由的权限,指定路由有哪些权限可以查看。只初始化通用的路由,需要权限才能访问的路由没有被加入路由表内。在登陆后或者其他方式获取用户角色后,通过角色去遍历路由表,获取该角色可以访问的路由表,生成路由表,再通过 router.addRoutes
添加到路由实例,实现权限的过滤。
缺点: 权限相对不自由,如果后台改动角色,前台也需要跟着改动。适合角色较固定的系统
实现
- 在项目配置将系统内权限模式改为
ROLE
模式
// ! 改动后需要清空浏览器缓存
const setting: ProjectConfig = {
// 权限模式
permissionMode: PermissionModeEnum.ROLE,
};
- 在路由表配置路由所需的权限,如果不配置,默认可见(见注释)
import type { AppRouteModule } from "/@/router/types";
import { getParentLayout, LAYOUT } from "/@/router/constant";
import { RoleEnum } from "/@/enums/roleEnum";
import { t } from "/@/hooks/web/useI18n";
const permission: AppRouteModule = {
path: "/permission",
name: "Permission",
component: LAYOUT,
redirect: "/permission/front/page",
meta: {
icon: "ion:key-outline",
title: t("routes.demo.permission.permission"),
},
children: [
{
path: "front",
name: "PermissionFrontDemo",
component: getParentLayout("PermissionFrontDemo"),
meta: {
title: t("routes.demo.permission.front"),
},
children: [
{
path: "auth-pageA",
name: "FrontAuthPageA",
component: () =>
import("/@/views/demo/permission/front/AuthPageA.vue"),
meta: {
title: t("routes.demo.permission.frontTestA"),
roles: [RoleEnum.SUPER],
},
},
{
path: "auth-pageB",
name: "FrontAuthPageB",
component: () =>
import("/@/views/demo/permission/front/AuthPageB.vue"),
meta: {
title: t("routes.demo.permission.frontTestB"),
roles: [RoleEnum.TEST],
},
},
],
},
],
};
export default permission;
- 在路由钩子内动态判断
详细代码见 src/router/guard/permissionGuard.ts
// 这里只列举了主要代码
const routes = await permissionStore.buildRoutesAction();
routes.forEach((route) => {
router.addRoute(route as unknown as RouteRecordRaw);
});
const redirectPath = (from.query.redirect || to.path) as string;
const redirect = decodeURIComponent(redirectPath);
const nextData =
to.path === redirect ? { ...to, replace: true } : { path: redirect };
permissionStore.setDynamicAddedRoute(true);
next(nextData);
permissionStore.buildRoutesAction 用于过滤动态路由,详细代码见 src/store/modules/permission.ts
// 主要代码
if (permissionMode === PermissionModeEnum.ROLE) {
const routeFilter = (route: AppRouteRecordRaw) => {
const { meta } = route;
const { roles } = meta || {};
if (!roles) return true;
return roleList.some((role) => roles.includes(role));
};
routes = filter(asyncRoutes, routeFilter);
routes = routes.filter(routeFilter);
// Convert multi-level routing to level 2 routing
routes = flatMultiLevelRoutes(routes);
}
动态更换角色
系统提供 usePermission 方便角色相关操作
import { usePermission } from "/@/hooks/web/usePermission";
import { RoleEnum } from "/@/enums/roleEnum";
export default defineComponent({
setup() {
const { changeRole } = usePermission();
// 更换为test角色
// 动态更改角色,传入角色名称,可以是数组
changeRole(RoleEnum.TEST);
return {};
},
});
细粒度权限
函数方式
usePermission 还提供了按钮级别的权限控制。
<template>
<a-button
v-if="hasPermission([RoleEnum.TEST, RoleEnum.SUPER])"
color="error"
class="mx-4"
>
拥有[test,super]角色权限可见
</a-button>
</template>
<script lang="ts">
import { usePermission } from "/@/hooks/web/usePermission";
import { RoleEnum } from "/@/enums/roleEnum";
export default defineComponent({
setup() {
const { hasPermission } = usePermission();
return { hasPermission };
},
});
</script>
组件方式
具体查看权限组件使用
指令方式
提示
指令方式不能动态更改权限
<a-button v-auth="RoleEnum.SUPER" type="primary" class="mx-4">
拥有super角色权限可见</a-button
>
后台动态获取
实现原理: 是通过接口动态生成路由表,且遵循一定的数据结构返回。前端根据需要处理该数据为可识别的结构,再通过 router.addRoutes
添加到路由实例,实现权限的动态生成。
实现
- 在项目配置将系统内权限模式改为
BACK
模式
// ! 改动后需要清空浏览器缓存
const setting: ProjectConfig = {
// 权限模式
permissionMode: PermissionModeEnum.BACK,
};
- 路由拦截,与角色权限模式一致
permissionStore.buildRoutesAction 用于过滤动态路由,详细代码见 /@/store/modules/permission.ts
// 主要代码
if (permissionMode === PermissionModeEnum.BACK) {
const { createMessage } = useMessage();
createMessage.loading({
content: t("sys.app.menuLoading"),
duration: 1,
});
// !Simulate to obtain permission codes from the background,
// this function may only need to be executed once, and the actual project can be put at the right time by itself
let routeList: AppRouteRecordRaw[] = [];
try {
this.changePermissionCode();
routeList = (await getMenuList()) as AppRouteRecordRaw[];
} catch (error) {
console.error(error);
}
// Dynamically introduce components
routeList = transformObjToRoute(routeList);
// Background routing to menu structure
const backMenuList = transformRouteToMenu(routeList);
this.setBackMenuList(backMenuList);
routeList = flatMultiLevelRoutes(routeList);
routes = [PAGE_NOT_FOUND_ROUTE, ...routeList];
}
getMenuList 返回值格式
返回值由多个路由模块组成
注意
后端接口返回的数据中必须包含PageEnum.BASE_HOME
指定的路由(path 定义于src/enums/pageEnum.ts
)
[
{
path: "/dashboard",
name: "Dashboard",
component: "/dashboard/welcome/index",
meta: {
title: "routes.dashboard.welcome",
affix: true,
icon: "ant-design:home-outlined",
},
},
{
path: "/permission",
name: "Permission",
component: "LAYOUT",
redirect: "/permission/front/page",
meta: {
icon: "carbon:user-role",
title: "routes.demo.permission.permission",
},
children: [
{
path: "back",
name: "PermissionBackDemo",
meta: {
title: "routes.demo.permission.back",
},
children: [
{
path: "page",
name: "BackAuthPage",
component: "/demo/permission/back/index",
meta: {
title: "routes.demo.permission.backPage",
},
},
{
path: "btn",
name: "BackAuthBtn",
component: "/demo/permission/back/Btn",
meta: {
title: "routes.demo.permission.backBtn",
},
},
],
},
],
},
];
动态更换菜单
系统提供 usePermission 方便角色相关操作
import { usePermission } from "/@/hooks/web/usePermission";
import { RoleEnum } from "/@/enums/roleEnum";
export default defineComponent({
setup() {
const { changeMenu } = usePermission();
// 更改菜单的实现需要自行去修改
changeMenu();
return {};
},
});
细粒度权限
函数方式
usePermission 还提供了按钮级别的权限控制。
<template>
<a-button
v-if="hasPermission(['20000', '2000010'])"
color="error"
class="mx-4"
>
拥有[20000,2000010]code可见
</a-button>
</template>
<script lang="ts">
import { usePermission } from "/@/hooks/web/usePermission";
import { RoleEnum } from "/@/enums/roleEnum";
export default defineComponent({
setup() {
const { hasPermission } = usePermission();
return { hasPermission };
},
});
</script>
组件方式
具体查看权限组件使用
指令方式
提示
指令方式不能动态更改权限
<a-button v-auth="'1000'" type="primary" class="mx-4">
拥有code ['1000']权限可见
</a-button>
如何初始化 code
通常,如需做按钮级别权限,后台会提供相应的 code,或者类型的判断标识。这些编码只需要在登录后获取一次即可。
import { getPermCodeByUserId } from "/@/api/sys/user";
import { permissionStore } from "/@/store/modules/permission";
async function changePermissionCode(userId: string) {
// 从后台获取当前用户拥有的编码
const codeList = await getPermCodeByUserId({ userId });
permissionStore.commitPermCodeListState(codeList);
}