Files
vue-element-plus-admin/docs/docs/guide/role.md
kailong321200875 b9f96d485f release: 2.0.0发布
2021-10-24 12:25:30 +08:00

6.8 KiB
Raw Blame History

权限配置

介绍

在实际开发中,我们经常需要根据项目需求进行一个权限的控制,例如:菜单的权限,页面的按钮权限,这样做的好处是可以让每个用户都能专注于自己的业务。

本系项目前提供了两种权限控制的示例,当然,都是比较简单的,只是给开发者提供一点思路和借鉴而已。一般来讲,权限控制可以是前端控制,也可以是后端控制。两种都有自个的优势:

前端控制权限,那本质上就是以静态路由表为准,后端接口只提供简单的配置规则,然后有前端进行循环遍历,筛选出所需要的路由,然后进行动态路由的生成。优势在于比较快捷,所有的配置都已经在静态路由表中配置好了。劣势就是不太灵活,假如需要换下菜单的顺序或者更改菜单的名称,都需要前端这边修改完之后重新打包部署。

前端控制权限

我们可以看下本项目的前端路由控制逻辑,具体代码在src/views/role-demo/role/components/InfoWrite.vue

前端控制的例子的界面相对简单,我们直接看保存之后的逻辑:

// 获取所有被选中节点,由于是前端渲染,所以只要保存一维数组就行
form.checkedNodes = (tree.value as any).getCheckedNodes(false, true)
console.log(JSON.stringify(form.checkedNodes))
// 获取所有被选中的keys便于渲染是否选中
form.checkedkeys = (tree.value as any).getCheckedKeys()
console.log(JSON.stringify(form.checkedkeys))

我们通过Element-ui Tree组件提供的API可以轻松的获取到被选中的数据。之后就可以调用后端接口进行权限数据的保存了当然开发者可以根据实际情况进行扩展流程大体是一致的。

之后,用户登录的时候,我们就可以拿到对应的权限数据,进行动态渲染了。权限过滤的逻辑主要在src/store/modules/permission.ts中的generateRoutes方法:

// 路由过滤,主要用于权限控制
function generateRoutesFn(routes: AppRouteRecordRaw[], basePath = '/'): AppRouteRecordRaw[] {
  const res: AppRouteRecordRaw[] = []

  for (const route of routes) {
    // skip some route
    if (route.meta && route.meta.hidden && !route.meta.showMainRoute) {
      console.log(route)
      continue
    }

    let onlyOneChild: Nullable<string> = null

    if (route.children && route.children.length === 1 && !route.meta.alwaysShow) {
      onlyOneChild = (
        isExternal(route.children[0].path)
          ? route.children[0].path
          : path.resolve(path.resolve(basePath, route.path), route.children[0].path)
      ) as string
    }

    let data: Nullable<IObj> = null

    // 如不需要路由权限,可注释以下逻辑
    // 权限过滤,通过获取登录信息里面的角色权限,动态的渲染菜单。
    const list = wsCache.get(appStore.getUserInfo).checkedNodes
    // 开发者可以根据实际情况进行扩展
    for (const item of list) {
      // 通过路径去匹配
      if (isExternal(item.path) && (onlyOneChild === item.path || route.path === item.path)) {
        data = Object.assign({}, route)
      } else {
        const routePath = path.resolve(basePath, onlyOneChild || route.path)
        if (routePath === item.path || (route.meta && route.meta.followRoute === item.path)) {
          data = Object.assign({}, route)
        }
      }
    }
    // 如不需要路由权限,解注释下面一行
    // data = Object.assign({}, route)

    // recursive child routes
    if (route.children && data) {
      data.children = generateRoutesFn(route.children, path.resolve(basePath, data.path))
    }
    if (data) {
      res.push(data as AppRouteRecordRaw)
    }
  }
  return res
}

::: tip 提示 由于是前端控制所以我们存储在后端的权限数据可以是一维数组或者直接拿被选中的keys进行过滤这个也是完全根据实际情况来的。 :::

后端控制权限

由于在静态路由表配置中,我们会把一些相关展示配置放在meta中,如果是以后端控制为主,我们项目中的静态路由表基本上没什么作用了。我们需要把meta中的配置放到后端中,有后端去统一管理。基本上,前端静态路由表就没什么用了。

我们可以看下本项目的后路由控制逻辑,具体代码在src/views/role-demo/role/components/InfoWrite2.vue

由于是后端控制为主,所以存储在后端就是以树形的方式存储了,便于我们拿到数据之后直接渲染,格式大致如下:

{
  path: '/components-demo',
  component: '#',
  redirect: '/components-demo/echarts',
  name: 'ComponentsDemo',
  meta: {
    title: '功能组件',
    icon: 'component',
    alwaysShow: true
  },
  children: [
    {
      path: 'echarts',
      component: 'views/components-demo/echarts/index',
      name: 'EchartsDemo',
      meta: {
        title: '图表'
      }
    }
  ]
}

component不再是直接加载路由文件了,而是以字符串的形式存在,之后当我们在渲染的时候,才会动态去加载对应的路由文件。

权限过滤的逻辑主要在src/store/modules/permission.ts中的getFilterRoutes方法:

// 模拟后端过滤路由
function getFilterRoutes(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] {
  const res: AppRouteRecordRaw[] = []

  for (const route of routes) {
    const data: AppRouteRecordRaw | IObj = {
      path: route.path,
      name: route.name,
      redirect: route.redirect
    }
    data.meta = Object.assign({}, route.meta || {}, { title: route.meta.title })
    if (route.component) {
      // 动态加载路由文件,可根据实际情况进行自定义逻辑
      data.component = (
        (route.component as any) === '#'
          ? Layout
          : (route.component as any).includes('##')
          ? getParentLayout((route.component as any).split('##')[1])
          : modules[`../../${route.component}.vue`]
      ) as any
    }
    // recursive child routes
    if (route.children) {
      data.children = getFilterRoutes(route.children)
    }
    res.push(data as AppRouteRecordRaw)
  }
  return res
}

::: tip 提示 后端控制的权限,我们只需要关注component的动态生成就行。 :::

总结

其实我们不用太纠结到底是前端控制还是后端控制,主要还是要根据实际情况来。一般来讲,大多数都是比较推崇使用后端控制,这样就不需要前端修改完配置之后在重新打包部署。

本项目中的例子是相对比较简单的,因为不想给出太复杂的而让开发者有无从下手的感觉。但是流程基本上是一致的,开发者是可以在本项目原有的基础上去扩展。