概述

在最近的后台管理系统中,涉及到了角色权限分配的问题,这也是对于管理系统中较常见的需求,往往需要前后端配合来实现不同角色有不同权限。


权限分配

主要采用基于角色的权限分配模型(RABC)

角色权限架构_权限分配


通过用户登录后,后端返回用户的个人信息,其中包括了用户对应的角色,再通过对应的角色去获取对应角色的所有权限,这样前端就能获取到对应登录角色的功能菜单了。这主要是后端的配置。


路由配置

针对传过来的菜单信息,前端需要进行对应的路由注册,可是怎样进行注册显得更合理更优雅呢。

方案一:直接配置所有路由组件(不推荐)
当然,我们可以直接在routes中将所有的菜单路由进行映射,因为每个角色菜单不一样,有些功能本来就选不到。
优点: 方便简单
缺点: 虽然菜单栏选不到,但是可以通过人为的在搜索栏进行跳转,这明显是不合理的

方案二:前端根据角色提前创建好对应的路由映射数组(不推荐)
前端可以提前创建好角色与需要注册的路由的对应关系,然后可以直接通过获取到的角色再去其中进行查找,遍历注册
优点:容易理解,简单
缺点: 不方便,由于需要前端配置,如果后端新增角色,那么前端还需要修改对应的代码

方案三:根据有传过来的菜单进行动态注册(推荐)
由于每个角色的菜单权限是不一样的,所以如果能够通过菜单来进行动态注册路由无疑是一种优雅合理的方式,这里也具体说下实现
实现思路
将所有菜单的路由映射获取,然后与已得到的角色菜单跳转路径进行对比,相同的则取出,待全取出后再统一去进行注册。

如下:main文件下包含了所有的菜单分类

角色权限架构_权限分配_02

,其中ts文件为路由映射关系

1.读取所有的路由映射

const routes: RouteRecordRaw[] = []
  // 1.读取main文件夹下的所有路由路径
  const RouteFiles = require.context('../router/main', true, /\.ts$/)//(路径,是否递归查询,所有.ts结尾的文件)
  const allRoutes: RouteRecordRaw[] = []
  RouteFiles.keys().forEach((routeUrl) => {
    const route = require('../router/main' + routeUrl.substring(1))//获取路由
    allRoutes.push(route.default)//加入所有路由数组
  })

2.与菜单功能进行对比,得到需要进行注册的路由

function getRouteFromTarget(target: IUserMenuItem[]) {
    target.forEach((item) => {
      if (item.type === 2) {
        const route = allRoutes.find((r) => {//找到相同路由
          return r.path === item.url
        })
        if (route) {
          routes.push(route)//存在相同路由则保存
        }
      } else {
        if (item.children && item.children.length)
          getRouteFromTarget(item.children)
      }
    })
  }
  getRouteFromTarget(userMenu)

这里通过递归的方式进行对比,因为菜单是多级的,只对比具体可点击的功能,这里type为2时说明是一级菜单了,当然这是后端对菜单类型的配置,需要与后端协调。

3.注册路由

routes.forEach((route) => {
          router.addRoute('main', route)
        })