First of all, there are two general requirements for permission management: page permission and button permission.

  1. Permission management Generally requires page permission and button permission management

  2. Specific implementation of the back-end and front-end two schemes:

The front-end solution configures all routing information in the front end, requires users to log in through the route guard, and filters routing tables based on roles after users log in. For example, I will configure an asyncRoutes array, add a roles field to the meta of the route for the page requiring authentication, and take the intersection of the two after obtaining the user role. If the result is not empty, it indicates that the user can access. After the filtering process is complete, the remaining routes are the pages that can be accessed by the user. You can dynamically addRoutes using router.addRoutes(accessRoutes).

In the back-end solution, all page routing information is stored in the database. When users log in, they query all page routing information that they can access according to their roles and return it to the front end, which then dynamically adds routing information through addRoutes.

The control of button permission usually implements an instruction, such as V-Permission, which passes the role required by the button to the V-Permission instruction by value. In the moutned hook of the instruction, it can determine whether there is an intersection between the current user role and the button. If there is, the button will be reserved, and if there is not, the button will be removed.

  1. The advantages of pure front-end solution is simple to implement, without the need for additional rights management pages, but the maintenance of the problem is relatively large, there are new pages and role needs to modify the front-end code to repackage deployment; The server solution does not exist this problem, through the special role and permission management page, configuration page and button permission information to the database, the application each login to obtain the latest routing information, can be described as once and for all!

Route guard permission.js:

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'

NProgress.configure({ showSpinner: false }) // NProgress Configuration

const whiteList = ['/login'.'/auth-redirect'] // no redirect whitelist

router.beforeEach(async(to, from, next) => {
  // start progress bar
  NProgress.start()

  // set page title
  document.title = getPageTitle(to.meta.title)

  // determine whether the user has logged in
  const hasToken = getToken()

  if (hasToken) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
      NProgress.done() // hack: https://github.com/PanJiaChen/vue-element-admin/pull/2939
    } else {
      // determine whether the user has obtained his permission roles through getInfo
      const hasRoles = store.getters.roles && store.getters.roles.length > 0
      if (hasRoles) {
        next()
      } else {
        try {
          // get user info
          // note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
          const { roles } = await store.dispatch('user/getInfo')

          // generate accessible routes map based on roles
          const accessRoutes = await store.dispatch('permission/generateRoutes', roles)

          // dynamically add accessible routes
          router.addRoutes(accessRoutes)

          // hack method to ensure that addRoutes is complete
          // set the replace: true, so the navigation will not leave a history recordnext({ ... to,replace: true})}catch (error) {
          // remove token and go to login page to re-login
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login? redirect=${to.path}`)
          NProgress.done()
        }
      }
    }
  } else {
    /* has no token*/

    if(whiteList.indexOf(to.path) ! = = -1) {
      // in the free login whitelist, go directly
      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      next(`/login? redirect=${to.path}`)
      NProgress.done()
    }
  }
})

router.afterEach(() = > {
  // finish progress bar
  NProgress.done()
})
Copy the code

Route generates ## permission.js:

import { asyncRoutes, constantRoutes } from '@/router'

/**
 * Use meta.role to determine if the current user has permission
 * @param roles
 * @param route* /
function hasPermission(roles, route) {
  if (route.meta && route.meta.roles) {
    return roles.some(role= > route.meta.roles.includes(role))
  } else {
    return true}}/**
 * Filter asynchronous routing tables by recursion
 * @param routes asyncRoutes
 * @param roles* /
export function filterAsyncRoutes(routes, roles) {
  const res = []

  routes.forEach(route= > {
    consttmp = { ... route }if (hasPermission(roles, tmp)) {
      if (tmp.children) {
        tmp.children = filterAsyncRoutes(tmp.children, roles)
      }
      res.push(tmp)
    }
  })

  return res
}

const state = {
  routes: [].addRoutes: []}const mutations = {
  SET_ROUTES: (state, routes) = > {
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}

const actions = {
  generateRoutes({ commit }, roles) {
    return new Promise(resolve= > {
      let accessedRoutes
      if (roles.includes('admin')) {
        accessedRoutes = asyncRoutes || []
      } else {
        accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      }
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}
Copy the code

Dynamic appending route ## permission.js:

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'

NProgress.configure({ showSpinner: false }) // NProgress Configuration

const whiteList = ['/login'.'/auth-redirect'] // no redirect whitelist

router.beforeEach(async(to, from, next) => {
  // start progress bar
  NProgress.start()

  // set page title
  document.title = getPageTitle(to.meta.title)

  // determine whether the user has logged in
  const hasToken = getToken()

  if (hasToken) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
      NProgress.done() // hack: https://github.com/PanJiaChen/vue-element-admin/pull/2939
    } else {
      // determine whether the user has obtained his permission roles through getInfo
      const hasRoles = store.getters.roles && store.getters.roles.length > 0
      if (hasRoles) {
        next()
      } else {
        try {
          // get user info
          // note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
          const { roles } = await store.dispatch('user/getInfo')

          // generate accessible routes map based on roles
          const accessRoutes = await store.dispatch('permission/generateRoutes', roles)

          // dynamically add accessible routes
          router.addRoutes(accessRoutes)

          // hack method to ensure that addRoutes is complete
          // set the replace: true, so the navigation will not leave a history recordnext({ ... to,replace: true})}catch (error) {
          // remove token and go to login page to re-login
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login? redirect=${to.path}`)
          NProgress.done()
        }
      }
    }
  } else {
    /* has no token*/

    if(whiteList.indexOf(to.path) ! = = -1) {
      // in the free login whitelist, go directly
      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      next(`/login? redirect=${to.path}`)
      NProgress.done()
    }
  }
})

router.afterEach(() = > {
  // finish progress bar
  NProgress.done()
})
Copy the code

How can the routing information returned by the server be added to the router?

// Front-end component name and component mapping table
const map = {
  // xx: require('@/views/xx.vue'). Default
  xx: () = > import('@/views/xx.vue') // Asynchronous
 }
 // asyncRoutes returned by the server
 const asyncRoutes = [
  { path: '/xx'.component: 'xx'. }]// Iterate over asyncRoutes and replace Component with map[Component]
function mapComponent(asyncRoutes) {
  asyncRoutes.forEach(route= > {
    route.component = map[route.component];
    if(route.children) {
      route.children.map(child= > mapComponent(child))
    }
  })
 }
 mapComponent(asyncRoutes)
Copy the code