需求

最近在项目中遇到权限控制的问题,由于自己接触前端也不久,对于路由这方面知识还有所欠缺,因此一直在网上找寻各种解决方案。最终还是看到花裤衩大佬的博客,终于对前端实现路由权限控制有了些许思路。
传送门:手摸手,带你用vue撸后台

下面这篇博客也有所参考:基于Vue实现后台系统权限控制

先说说我在项目中需要实现的效果:
由超级管理员在页面中进行选择,从而控制其他用户访问各页面的权限。系统再根据该用户所拥有的的权限来进行路由控制(对路由进行过滤)。

基本思路

1、创建vue实例的时将vue-router挂载,但vue-router初始时只挂载登录页面(或是其他不需要权限的页面)。

2、当用户登录后,获取后台返回的该用户能访问的页面数组role,将role和路由表每个页面的需要的权限作比较,生成最终用户可访问的路由表。

3、调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由。

4、使用vuex管理路由表,根据vuex中可访问的路由渲染侧边栏组件。

在我的项目中将后台返回的数组role定义为该用户可访问的页面,而不是该用户的职位(身份),因为在此系统中可以由超级管理员随意编辑用户所能访问的页面范围,而不仅仅是通过身份来定义权限。

具体实现

1、在router.js中将需要权限的每个页面的路由(异步挂载的路由asyncRouterMap)添加属于该页面的标签。(用于与role数组作比较)

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

Vue.use(Router)
import Layout from '../views/layout/Layout'

export const constantRouterMap = [
  { path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true,
    meta: { title: '系统登录' },
  {
    path: '/',
    redirect: '/login'
  }
]

export default new Router({
  // mode: 'history', //后端支持可开
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRouterMap
})

// 异步挂载的路由
// 动态需要根据权限加载的路由表
export const asyncRouterMap = [
  {
    path: '/home',
    component: Layout,
    children: [
      {
        path: 'index',
        name: 'home',
        component: () => import('@/views/home/index'),
        meta: { title: '首  页',
          icon: 'home',
          role: ['home', 'admin']
        }
      }
    ]
  },
  {
    path: '/schedule',
    component: Layout,
    name: 'schedule',
    children: [
      {
        path: 'ongoing',
        name: 'ongoing',
        component: () => import('@/views/schedule/ongoing'),
        meta: { title: '工作进度', icon: 'ongoing',
          role: ['ongoing', 'admin']
        }
      }
    ]
  },
  {
    path: '/schedule',
    component: Layout,
    name: 'schedule',
    children: [
      {
        path: 'follow_up',
        name: 'follow_up',
        component: () => import('@/views/schedule/follow_up'),
        meta: { title: '后续事务', icon: 'tree',
          role: ['follow', 'admin']
        }
      }
    ]
  },
  {
    path: '/data_manage',
    component: Layout,
    name: 'data_manage',
    children: [
      {
        path: 'data_collect',
        name: 'data_collect',
        component: () => import('@/views/data_package/gather'),
        meta: { title: '数据包收集', icon: 'data_gather', 
          role: ['data_collect', 'admin']
        }
      }

    ]
  },
  {
    path: '/data_manage',
    component: Layout,
    name: 'data_manage',
    children: [
      {
        path: 'data_query',
        name: 'data_query',
        component: () => import('@/views/data_package/search_output'),
        meta: { title: '查询与导出', icon: 'output', 
          role: ['search', 'output', 'admin']
        }
      }

    ]
  },

  {
    path: '/template_manage',
    component: Layout,
    children: [
      {
        path: 'index',
        name: 'template_manage',
        component: () => import('@/views/document/index'),
        meta: { title: '文件管理', icon: 'document', requireAuth: true,
          role: ['admin', 'document'] }
      }
    ]
  },
  {
    path: '/user_manage',
    component: Layout,
    children: [
      {
        path: 'index',
        component: () => import('@/views/user_manage/index'),
        name: '用户管理',
        meta: { title: '用户管理', icon: 'user_manage', role: ['admin'] } // 页面需要的权限
      }]
  },
  { path: '*', redirect: '/404', hidden: true }
]

通过meta标签来标示该页面能访问的权限有哪些。如meta: { role: [‘admin’,‘home’] }表示该页面只有admin和拥有home页面进入权限的用户才能有资格进入。

注意事项:这里有一个需要非常注意的地方就是 404 页面一定要最后加载,如果放在constantRouterMap一同声明了404,后面的所以页面都会被拦截到404。

2、当用户登录后,获取用role,将role和路由表每个页面的需要的权限作比较,调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由,生成最终用户可访问的路由表。路由表存在vuex里面

\permission.js

import router from './router'
import store from './store'
import NProgress from 'nprogress' // Progress 进度条
import 'nprogress/nprogress.css'// Progress 进度条样式
import { Message } from 'element-ui'
import { getToken } from '@/utils/auth' // 验权

const whiteList = ['/login', '/'] // 不重定向白名单
router.beforeEach((to, from, next) => {
  NProgress.start()
  if (sessionStorage.getItem('name') !== null) {
    if (!store.getters.routers) {
      const permission = sessionStorage.getItem('role')
      store.dispatch('GenerateRoutes', { permission }).then(() => { // 生成可访问的路由表
        router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
        next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
      })
    } else {
      next()
      NProgress.done()
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next({
        path: '/login'
      })
    }
  }
 
router.afterEach(() => {
  console.log('跳转成功')
  NProgress.done() // 结束Progress
})

\store\modules\permission.js

import { asyncRouterMap, constantRouterMap } from '../../router/index'

function hasPermission(roles, route) {
  if (route.meta && route.meta.role) {
    return roles.some(role => route.meta.role.indexOf(role) >= 0)
  } else {
    return true
  }
}

const permission = {
  state: {
    routers: constantRouterMap,
    addRouters: []
  },
  mutations: {
    SET_ROUTERS: (state, routers) => {
      state.addRouters = routers
      state.routers = constantRouterMap.concat(routers)
    }
  },
  actions: {
    GenerateRoutes({ commit }, data) {
      return new Promise(resolve => {
        const { permission } = data
        const permissions = permission.split(',')
        const accessedRouters = asyncRouterMap.filter(v => {
          if (permissions.indexOf('admin') >= 0) return true
          if (hasPermission(permissions, v)) {
            if (v.children && v.children.length > 0) {
              v.children = v.children.filter(child => {
                if (hasPermission(permissions, child)) {
                  return child
                }
                return false
              })
              if (v.children.length === 0) { // 当children数组为空,即数组中的路由无权限被过滤掉
                return false
              } else {
                return v
              }
            } else {
              return false
            }
          }
          return false
        })
        commit('SET_ROUTERS', accessedRouters)
        resolve()
      })
    }
  }
}

export default permission

3、使用vuex管理路由表,根据vuex中可访问的路由渲染侧边栏组件(菜单)。
在这里插入图片描述

4、前端控制用户权限页面

权限控制已经实现,只需要再实现用于超级管理员编辑用户权限的页面即可。
在这里插入图片描述

后续

现在看起来好像实现起来一点都不复杂,但是鬼晓得我当时实现花了多久。有时候改着改着代码,页面就一片空白,把我给拒之页外(哭泣),然后又开始一步一步调试查看被堵在哪里。
往事不堪回首,总归算是有了一点点进步,继续努力吧!!!

Logo

前往低代码交流专区

更多推荐