ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Vue-Vben-Admin权限

2024-02-07 11:11:59  阅读:31  来源: 互联网

标签:


项目中集成了三种权限处理方式:

  1. 通过用户角色来过滤菜单(前端方式控制),菜单和路由分开配置
  2. 通过用户角色来过滤菜单(前端方式控制),菜单由路由配置自动生成
  3. 通过后台来动态生成路由表(后台方式控制)

#前端角色权限

实现原理: 在前端固定写死路由的权限,指定路由有哪些权限可以查看。只初始化通用的路由,需要权限才能访问的路由没有被加入路由表内。在登陆后或者其他方式获取用户角色后,通过角色去遍历路由表,获取该角色可以访问的路由表,生成路由表,再通过 router.addRoutes 添加到路由实例,实现权限的过滤。

缺点: 权限相对不自由,如果后台改动角色,前台也需要跟着改动。适合角色较固定的系统

#实现

  1. 在项目配置将系统内权限模式改为 ROLE 模式
// ! 改动后需要清空浏览器缓存
const setting: ProjectConfig = {
  // 权限模式
  permissionMode: PermissionModeEnum.ROLE,
};
  1. 在路由表配置路由所需的权限,如果不配置,默认可见(见注释)
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;
  1. 在路由钩子内动态判断

详细代码见 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>

组件方式

具体查看权限组件使用

指令方式

TIP

指令方式不能动态更改权限

<a-button v-auth="RoleEnum.SUPER" type="primary" class="mx-4"> 拥有super角色权限可见</a-button>

#后台动态获取

实现原理: 是通过接口动态生成路由表,且遵循一定的数据结构返回。前端根据需要处理该数据为可识别的结构,再通过 router.addRoutes 添加到路由实例,实现权限的动态生成。

#实现

  1. 在项目配置将系统内权限模式改为 BACK 模式
// ! 改动后需要清空浏览器缓存
const setting: ProjectConfig = {
  // 权限模式
  permissionMode: PermissionModeEnum.BACK,
};
  1. 路由拦截,与角色权限模式一致

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>

组件方式

具体查看权限组件使用

指令方式

TIP

指令方式不能动态更改权限

<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);
}

标签:
来源:

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[[email protected]]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有