1. 程式人生 > >vue 許可權管理

vue 許可權管理

今天來說說許可權管理,因為網上已經有很多關於這方面的很多內容,博主也是借鑑了網上的一些邏輯來寫的,主要也是說說前端實現許可權管理的一個思路,也是作為自己日後面對這樣的問題的一個解決方案。 首先需要了解的就是純前端是無法實現許可權管理的,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上面檢視。之前寫的許可權沒有問題,但是重新整理的時候,突然發現許可權需要重新整理的時候才會生效,技術有限,所以要是有同學發現程式碼有什麼問題,可以隨時告訴博主,當然博主也是會繼續踩坑下去,解決這個問題。