Authority
Permissions
The project integrates three ways of handling permissions:
- Filter the menu through user roles (front-end control), menu and routing are configured separately
- Filter the menu through user roles (front-end control), the menu is automatically generated by routing configuration
- Dynamically generate routing tables through the backend (backend control)
Front-end Role Permissions
Implementation principle: Write down the permissions of the route in the front end, and specify which permissions can be viewed by the route. Only general routes are initialized, and routes that require permissions to access are not added to the routing table. After logging in or obtaining user roles in other ways, traverse the routing table through roles, obtain the routing table that the role can access, generate a routing table, and then add it to the routing instance through router.addRoutes
to achieve permission filtering.
Disadvantages: The permissions are relatively unfree. If the backend changes roles, the frontend also needs to change accordingly. Suitable for systems with relatively fixed roles
Implementation
- Change the permission mode in the system to
ROLE
mode in [project configuration](./settings.md#project configuration)
// ! You need to clear your browser cache after making changes
const setting: ProjectConfig = {
// Permission mode
permissionMode: PermissionModeEnum.ROLE,
};
- Configure the permissions required for routing in the routing table. If not configured, it is visible by default (see comments)
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;
- Dynamic judgment in the routing hook
See src/router/guard/permissionGuard.ts for detailed code
// Only the main code is listed here
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 is used to filter dynamic routes, see [src/store/modules/permission.ts](https://github.com/vbenjs/vue-vben-admin/tree/ for detailed code) main/src/store/modules/permission.ts)
// Main code
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);
}
Dynamically change roles
The system provides usePermission to facilitate role-related operations
import { usePermission } from "/@/hooks/web/usePermission";
import { RoleEnum } from "/@/enums/roleEnum";
export default defineComponent({
setup() {
const { changeRole } = usePermission();
// Change to test role
// Change the role dynamically, pass in the role name, which can be an array
changeRole(RoleEnum.TEST);
return {};
},
});
Fine-grained permissions
Function mode
usePermission also provides button-level permission control.
<template>
<a-button
v-if="hasPermission([RoleEnum.TEST, RoleEnum.SUPER])"
color="error"
class="mx-4"
>
Has [test, super] role permissions visible
</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>
Component method
See Permission Component Usage for details
Directive method
Tips
The directive method cannot dynamically change permissions
<a-button v-auth="RoleEnum.SUPER" type="primary" class="mx-4">
Visible with super role permission</a-button
>
Dynamically Obtained from the Backend
Implementation principle: It is to dynamically generate a routing table through the interface and return it in accordance with a certain data structure. The front end processes the data into a recognizable structure as needed, and then adds it to the routing instance through router.addRoutes
to achieve dynamic generation of permissions.
Implementation
- Change the permission mode in the system to
BACK
mode in [project configuration](./settings.md#project configuration)
// ! You need to clear your browser cache after making changes
const setting: ProjectConfig = {
// Permission mode
permissionMode: PermissionModeEnum.BACK,
};
- Route interception, consistent with role permission mode
permissionStore.buildRoutesAction is used to filter dynamic routes, see detailed code /@/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 return value format
The return value is composed of multiple routing modules
Warning
The data returned by the backend interface must contain the route specified by PageEnum.BASE_HOME
(path is defined in 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",
},
},
],
},
],
},
];
Change the menu dynamically
The system provides usePermission to facilitate role-related operations
import { usePermission } from "/@/hooks/web/usePermission";
import { RoleEnum } from "/@/enums/roleEnum";
export default defineComponent({
setup() {
const { changeMenu } = usePermission();
// The implementation of the change menu needs to be modified by itself
changeMenu();
return {};
},
});
Fine-grained permissions
Function mode
usePermission also provides button-level permission control.
<template>
<a-button
v-if="hasPermission(['20000', '2000010'])"
color="error"
class="mx-4"
>
Visible with [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>
Component method
See Permission Component Usage for details
Directive method
Tips
The directive method cannot dynamically change permissions
<a-button v-auth="'1000'" type="primary" class="mx-4">
Visible with code ['1000'] permission
</a-button>
How to initialize code
Usually, if you need to do button-level permissions, the backend will provide the corresponding code or type judgment identification. These codes only need to be obtained once after logging in.
import { getPermCodeByUserId } from "/@/api/sys/user";
import { permissionStore } from "/@/store/modules/permission";
async function changePermissionCode(userId: string) {
// Get the code that the current user has from the backend
const codeList = await getPermCodeByUserId({ userId });
permissionStore.commitPermCodeListState(codeList);
}