1. 程式人生 > 程式設計 >Vue路由許可權控制解析

Vue路由許可權控制解析

前言

本人在公司主要負責中後臺系統的開發,其中路由和許可權校驗算是非常重要且最為基本的一環。實際開發專案中,關於登入和路由許可權的控制參照了vue-element-admin這個明星專案,並在此基礎上基於業務進行了整合,接下來我會以這個專案為例,仔細地剖析整個路由和許可權校驗的過程,也算是對這個知識點的一些總結。

專案總體目錄結構

進入今天主題之前,我們先來梳理下整個專案,src目錄下的。

  • api: 介面請求
  • assets: 靜態資源
  • components: 通用元件
  • directive: 自定義指令
  • filters: 自定義過濾器
  • icons: 圖示
  • layout: 佈局元件(頁面架構核心)
  • router: 路由配置(路由許可權核心模組)
  • store: 狀態管理
  • styles: 樣式檔案
  • utils: 工具方法
  • views: 頁面元件
  • permission.js 許可權管理

對這專案感興趣的同學可以自行,有針對性地學習,除了路由許可權校驗的功能以外,也包含了很多有意思的功能,相信能夠學到不少東西。

路由許可權控制邏輯

路由處理流程圖

Vue路由許可權控制解析

路由處理原始碼分析

我們先找到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

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

const whiteList = ['/login','/auth-redirect'] // 白名單配置

router.beforeEach(async(to,from,next) => {
 // start progress bar
 NProgress.start()
 // 有token
 if (hasToken) {
 if (to.path === '/login') 
  // 如果當前路徑為/login,重定向到首頁
  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 {
   // 獲取使用者資訊
   const { roles } = await store.dispatch('user/getInfo')
   // 根據使用者的角色,動態生成路由
   const accessRoutes = await store.dispatch('permission/generateRoutes',roles)
   // 動態新增路由 (將基本的路由資訊跟動態路由進行合併)
   router.addRoutes(accessRoutes)
   // 繼續訪問
   next({ ...to,replace: true })
  } catch (error) {
   // 刪除token
   await store.dispatch('user/resetToken')
   Message.error(error || 'Has Error')
   // 重定向到登入頁面
   next(`/login?redirect=${to.path}`)
   NProgress.done()
  }
  }
 }
 } else {
 // 沒有token
 if (whiteList.indexOf(to.path) !== -1) {
  // 如果在白名單中,則不需要進行任何校驗,直接放行
  next()
 } else {
  // 如果不存在於白名單中,則重定向到登入頁面.
  next(`/login?redirect=${to.path}`)
  NProgress.done()
 }
 }
})

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

注意到,程式碼中的/login?redirect=${jto.path},這裡的redirect引數主要是用於,在使用者登入成功後進行跳轉的頁面路徑。具體功能在/views/login/index.vue檔案下

watch: {
 $route: {
 handler: function(route) {
  const query = route.query
  if (query) { //路由查詢引數
  this.redirect = query.redirect
  this.otherQuery = this.getOtherQuery(query)
  }
 },immediate: true
 }
},// methods下的:
handleLogin() { // 登入函式
 this.$refs.loginForm.validate(valid => {
 if (valid) { // 賬號密碼校驗成功後
  this.$store.dispatch('user/login',this.loginForm)
  .then(() => {
   // 直接跳轉到this.redirect 路徑的頁面
   this.$router.push({ path: this.redirect || '/',query: this.otherQuery })
   this.loading = false
  })
 } else {
  // ..
 }
 })
},

動態路由配置

我們先來看看路由的定義,在/src/router/index.js檔案下

export const constantRoutes = [ // 用來定義普通的路由配置,不需要訪問許可權的
 // 路由配置物件
]
export const asyncRoutes = [ // 通過路由元資訊meta.roles來設定訪問許可權,一般來說是個陣列
 {
 path: '/permission',component: Layout,redirect: '/permission/page',alwaysShow: true,// will always show the root menu
 name: 'Permission',meta: {
  title: 'Permission',icon: 'lock',roles: ['admin','editor'] // 通過roles設定路由的許可權
 },// ...
 }
]

動態新增路由時,本質上就是根據使用者的角色資訊在asyncRoutes路由配置陣列中進行路由篩選,找到相對應的路由,與constantRoutes合併生成最新的路由。

動態新增路由邏輯圖

Vue路由許可權控制解析

動態路由原始碼分析
程式碼入口: permission.js

const accessRoutes = await store.dispatch('permission/generateRoutes',roles)

permission/generateRoutes方法入口檔案:/strore/modules/permissions.js

import { asyncRoutes,constantRoutes } from '@/router'
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')) {
  // 如果包含了admin,則說明是admin,具有所有模組的訪問許可權
  accessedRoutes = asyncRoutes || [] 
  } else {
  // 如果不是管理員,則需要根據使用者角色roles和非同步路由進行篩選
  accessedRoutes = filterAsyncRoutes(asyncRoutes,roles)
  }
  // 將最終的結果存放到vuex中
  commit('SET_ROUTES',accessedRoutes)
  // resolve出去
  resolve(accessedRoutes)
 })
 }
}

對非同步路由進行篩選,並將最終的結果存放到vuex中,並將結果resolve出去

export function hasPermission(roles,route) {
 if (route.meta && route.meta.roles) { // 如果存在meta.roles
 // 只要meta.roles中存在與使用者角色列表中相同的值,則說明具有訪問許可權
 return roles.some(role => route.meta.roles.includes(role))
 } else {
 // 不存在meta或者是不存在meta.roles,則說明是通用模組,直接放行
 return true
 }
}

export function filterAsyncRoutes(routes,roles) {
 const res = []

 routes.forEach(route => {
 const tmp = { ...route }
 if (hasPermission(roles,tmp)) { // 相對路由陣列的每一項進行訪問許可權的判斷
  if (tmp.children) {
  // 如果存在children,則遞迴呼叫篩選函式
  tmp.children = filterAsyncRoutes(tmp.children,roles)
  }
  // 將處理好的路由配置放入到res中
  res.push(tmp)
 }
 })
 return res
}

最後回到/permission.js檔案中

const accessRoutes = await store.dispatch('permission/generateRoutes',roles)
// 這裡的accessRoutes就是篩選之後的路由,
// 最後通過route.addRoutes將constRoutes和accessRoutes進行合併,生成最終的訪問路由
router.addRoutes(accessRoutes)

擴充套件-按鈕許可權

路由許可權控制基本流程已經分析完,接下來我們也來看看專案裡的按鈕許可權控制的實現,實現也比較簡單。

基本用法
<div v-permission="['admin','editor']"></div>
import store from '@/store'

function checkPermission(el,binding) {
 const { value } = binding
 // 從store中拿到我們訪問介面後,取到使用者角色資訊
 const roles = store.getters && store.getters.roles

 if (value && value instanceof Array) { // 判斷傳入的值是不是陣列,規範化傳值
 if (value.length > 0) {
  const permissionRoles = value
  // 只要傳入的permissionRoles中,包含了roles其中的一個值即可,則代表有許可權
  const hasPermission = roles.some(role => {
  return permissionRoles.includes(role)
  })
  // 沒有許可權則進行刪除,不展示。
  // v-permission具體實現可以根據業務場景進行修改
  if (!hasPermission) {
  el.parentNode && el.parentNode.removeChild(el)
  }
 }
 } else {
 throw new Error(`need roles! Like v-permission="['admin','editor']"`)
 }
}

export default {
 inserted(el,binding) {
 checkPermission(el,binding)
 },update(el,binding)
 }
}

總結

存在token

存在使用者角色資訊,則說明該使用者的最終可訪問的路由已經生成,可以直接放行

不存在使用者資訊

1.呼叫獲取使用者資訊介面,獲取到使用者資訊,將使用者資訊存放到vuex中

2.判斷使用者角色

  • 如果是管理員則對所有模組具有訪問許可權
  • 非管理員,需要對非同步路由進行篩選,通過遍歷非同步路由,並通過meta.roles與使用者資訊比較,判斷使用者是否具有訪問許可權

3.將最終的可訪問路由存放到vuex中,最後通過router.addRoutes,整合最後的路由配置列表

不存在token

如果訪問路由在白名單下,則直接進行訪問

訪問路由不存在白名單下,則重定向到登入頁面 path: /login?redirect=/xxx,登入成功後則跳轉到/xxx對應的頁面

以上就是Vue路由許可權控制解析的詳細內容,更多關於Vue路由許可權控制的資料請關注我們其它相關文章!