今天来说说权限管理,因为网上已经有很多关于这方面的很多内容,博主也是借鉴了网上的一些逻辑来写的,主要也是说说前端实现权限管理的一个思路,也是作为自己日后面对这样的问题的一个解决方案。
首先需要了解的就是纯前端是无法实现权限管理的,vue的权限管理主要是基于路由实现的,当然这样的权限管理完全可以被绕过去的,而之所以在前端这么做,更多是为了用户体验,节省不必要的加载开销等等。
用vue来实现权限管理,主要就是基于这个新的vue的api,addRoutes的方法来实现的,当然权限管理也分为几个方面,博主今天主要就是说的页面级的权限,思路就是利用刚刚说的api来动态加载路由,我们将路由分为两个部分,一个是公共部分,即所有人都能看到的路由,另外一部分则是动态加载部分,这里也简单的将代码贴出来,大家参考一下就可以,网上的代码也是有很多

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export const constRouterMap = [
  {
    path: '/login',
    name: 'Login',
    meta: {
      title: '登录',
      keepAlive: false
    },
    component: () => import('@/views/Login.vue')
  },
  {
    path: '/layout',
    component: () => import('@/views/Layout.vue'),
    // redirect: '/login',
    // redirect: '/index',
    children: [
      {
        path: '/index',
        name: 'index',
        meta: {
          title: '首页',
          keepAlive: false
        },
        component: () => import('@/views/HomePage.vue')
      },
      {
        path: '/about',
        name: 'about',
        meta: {
          title: '开发备忘',
          keepAlive: false
        },
        component: () => import('@/views/About.vue')
      },
      {
        path: '/author',
        name: 'author',
        meta: {
          title: '关于作者',
          keepAlive: false
        },
        component: () => import('@/views/AboutAuthor.vue')
      },
      {
        path: '/weather',
        name: 'wether-fore',
        meta: {
          title: '天气预报',
          keepAlive: true
        },
        component: () => import('@/views/WeatherFore.vue')
      },
      {
        path: '/dynamic',
        name: 'dynamic-table',
        meta: {
          title: '动态表格',
          keepAlive: false
        },
        component: () => import('@/views/DynamicTable.vue')
      },
      {
        path: '/editable',
        name: 'editable',
        meta: {
          title: '可编辑表格',
          keepAlive: true
        },
        component: () => import('@/views/TableGrid.vue')
      }
    ]
  }    
]

export const asyncRouterMap = [
  {
    path: '/layout',
    component: () => import('@/views/Layout.vue'),
    children: [
      {
        path: '/access',
        name: 'access',
        component: () => import('@/views/AccessTest.vue'),
        meta: {
          title: '权限控制',
          roles: ['admin'],
          keepAlive: false
        }
      },
      {
        path: '/cube',
        name: 'cube',
        component: () => import('@/views/MagicCube.vue'),
        meta: {
          title: '魔幻立方',
          roles: ['admin'],
          keepAlive: false
        }
      }
    ]
  },
  {
    path: '/error',
    component: () => import('@/views/NotFound.vue')
  },
  {
    path: '*', redirect: '/error', hidden: true
  }
]

export default new Router({
  mode: 'history',
  routes: constRouterMap
})

看到这个代码应该知道,我们首先是先加载公共路由部分,然后再根据vue-router提供的路由导航钩子,当路由进行跳转的时候,向服务器请求用户的角色权限

import router from './router'
import store from '@/store/index'
import { Message } from 'element-ui'
const whiteList = ['/login']

router.beforeEach((to, from, next) => {
  if (sessionStorage.getItem('token')) {
    if (to.path === '/') {
      next('/index')
    } else {
      if (store.getters.roles.length === 0) {
        const roles = sessionStorage.getItem('roles')
        store.dispatch('permission/generateRoute', { roles })
          .then(() => {
            console.log(store.getters.addRoutes[0].children.length)
            router.addRoutes(store.getters.addRoutes)
            next({ ...to, replace: true })
          })
          .catch(err => {
            Message.error({ message: err })
          })
      } else {
        next()
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next('/login')
    }
  }
})

这里后台博主用了thinkjs的框架,博主现在还在整理,后续整理好,也会上传到github上面。在这个导航钩子上,我们利用了vuex来存储角色信息,每次登陆会进行判断,如果没有角色信息,就会去触发action来请求服务器,服务器返回角色字段后,我们将他存在store里面。另外根据上面的路由,我们将页面权限需要的角色放在路由的meta字段中,所以当我们拿到角色信息后,会和动态路由中的meta进行一一对比,需要注意的是,当路由有嵌套的children属性时,我们还需要将children里面的路由拿出来一一进行递归对比,这样如果服务器的角色信息,符合路由需要的权限,我们就将这个路由记录下来,最后组成需要添加的路由,利用addRoutes来形成最终的路由。

generateRoute ({ commit }, data) {
      return new Promise((resolve, reject) => {
        const roles = []
        roles.push(data.roles)
        let accessRouters
        console.log(roles.indexOf('admin') >= 0)
        if (roles.indexOf('admin') >= 0) {
          accessRouters = asyncRouterMap
        } else {
          accessRouters = filterAsyncRouter(asyncRouterMap, roles)
        }
        // console.log(accessRouters)
        commit('SET_ROUTERS', accessRouters)
        commit('SET_ROLES', roles)
        resolve()
      })
    },
function hasPermission (roles, route) {
  if (route.meta && route.meta.roles) {
    return roles.some(role => route.meta.roles.indexOf(role) >= 0)
  } else {
    return true
  }
}

function filterAsyncRouter (asyncRouter, roles) {
  const accessRouters = asyncRouter.filter(route => {
    if (hasPermission(roles, route)) {
      if (route.children && route.children.length) {
        route.children = filterAsyncRouter(route.children, roles)
      }
      return true
    }
    return false
  })
  // console.log(accessRouters)
  return accessRouters
}

结合前面的导航钩子就形成了最终的路由,当用户试图访问不存在的路由的时候,则会返回404页面,另外也简单的说下按钮及页面中视图显示的权限,其实也是利用了vue的自定义指令,我们给自定义指令传入需要的角色权限数组,然后和存储在store中的权限作对比,如果没有权限,则移除当前的DOM元素

import store from '@/store/index'

const permission = {
  inserted (el, binding, vnode) {
    const { value } = binding
    const roles = store.getters && store.getters.roles
    if (value && value instanceof Array && value.length > 0) {
      const permissionRoles = value
      const hasPermission = roles.some(role => {
        return permissionRoles.includes(role)
      })
      if (!hasPermission) {
        el.parentNode && el.parentNode.removeChild(el)
      }
    } else {
      throw new Error('roles is must be Array!')
    }
  }
}
export default permission

这些代码都是博主自己写的项目中的片段,完整的代码,大家有兴趣可以去GitHub上面查看。之前写的权限没有问题,但是重新整理的时候,突然发现权限需要刷新的时候才会生效,技术有限,所以要是有同学发现代码有什么问题,可以随时告诉博主,当然博主也是会继续踩坑下去,解决这个问题。

Logo

前往低代码交流专区

更多推荐