权限
项目中集成了三种权限处理方式:
- 通过用户角色来过滤菜单(前端方式控制),菜单和路由分开配置
- 通过用户角色来过滤菜单(前端方式控制),菜单由路由配置自动生成
- 通过后台来动态生成路由表(后台方式控制)
前端角色权限
实现原理: 在前端固定写死路由的权限,指定路由有哪些权限可以查看。只初始化通用的路由,需要权限才能访问的路由没有被加入路由表内。在登陆后或者其他方式获取用户角色后,通过角色去遍历路由表,获取该角色可以访问的路由表,生成路由表,再通过 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 返回值格式
返回值由多个路由模块组成
[ { 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);}