Vue路由權(quán)限控制解析
前言
本人在公司主要負(fù)責(zé)中后臺(tái)系統(tǒng)的開發(fā),其中路由和權(quán)限校驗(yàn)算是非常重要且最為基本的一環(huán)。實(shí)際開發(fā)項(xiàng)目中,關(guān)于登錄和路由權(quán)限的控制參照了vue-element-admin這個(gè)明星項(xiàng)目,并在此基礎(chǔ)上基于業(yè)務(wù)進(jìn)行了整合,接下來我會(huì)以這個(gè)項(xiàng)目為例,仔細(xì)地剖析整個(gè)路由和權(quán)限校驗(yàn)的過程,也算是對(duì)這個(gè)知識(shí)點(diǎn)的一些總結(jié)。
項(xiàng)目總體目錄結(jié)構(gòu)
進(jìn)入今天主題之前,我們先來梳理下整個(gè)項(xiàng)目,src目錄下的。
- api: 接口請(qǐng)求
- assets: 靜態(tài)資源
- components: 通用組件
- directive: 自定義指令
- filters: 自定義過濾器
- icons: 圖標(biāo)
- layout: 布局組件(頁面架構(gòu)核心)
- router: 路由配置(路由權(quán)限核心模塊)
- store: 狀態(tài)管理
- styles: 樣式文件
- utils: 工具方法
- views: 頁面組件
- permission.js 權(quán)限管理
對(duì)這項(xiàng)目感興趣的同學(xué)可以自行,有針對(duì)性地學(xué)習(xí),除了路由權(quán)限校驗(yàn)的功能以外,也包含了很多有意思的功能,相信能夠?qū)W到不少東西。
路由權(quán)限控制邏輯
路由處理流程圖

路由處理源碼分析
我們先找到permission.js文件,此處定義全局路由守衛(wèi),也是路由權(quán)限中非常關(guān)鍵的核心代碼。為方便大家閱讀,只摘取了跟路由相關(guān)的代碼
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')
// 如果當(dāng)前路徑為/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')
// 根據(jù)用戶的角色,動(dòng)態(tài)生成路由
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
// 動(dòng)態(tài)添加路由 (將基本的路由信息跟動(dòng)態(tài)路由進(jìn)行合并)
router.addRoutes(accessRoutes)
// 繼續(xù)訪問
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) {
// 如果在白名單中,則不需要進(jìn)行任何校驗(yàn),直接放行
next()
} else {
// 如果不存在于白名單中,則重定向到登錄頁面.
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
router.afterEach(() => {
// finish progress bar
NProgress.done()
})
注意到,代碼中的/login?redirect=${jto.path}, 這里的redirect參數(shù)主要是用于,在用戶登錄成功后進(jìn)行跳轉(zhuǎn)的頁面路徑。具體功能在/views/login/index.vue文件下
watch: {
$route: {
handler: function(route) {
const query = route.query
if (query) { //路由查詢參數(shù)
this.redirect = query.redirect
this.otherQuery = this.getOtherQuery(query)
}
},
immediate: true
}
},
// methods下的:
handleLogin() { // 登錄函數(shù)
this.$refs.loginForm.validate(valid => {
if (valid) { // 賬號(hào)密碼校驗(yàn)成功后
this.$store.dispatch('user/login', this.loginForm)
.then(() => {
// 直接跳轉(zhuǎn)到this.redirect 路徑的頁面
this.$router.push({ path: this.redirect || '/', query: this.otherQuery })
this.loading = false
})
} else {
// ..
}
})
},
動(dòng)態(tài)路由配置
我們先來看看路由的定義,在/src/router/index.js文件下
export const constantRoutes = [ // 用來定義普通的路由配置,不需要訪問權(quán)限的
// 路由配置對(duì)象
]
export const asyncRoutes = [ // 通過路由元信息meta.roles來設(shè)置訪問權(quán)限,一般來說是個(gè)數(shù)組
{
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設(shè)置路由的權(quán)限
},
// ...
}
]
動(dòng)態(tài)添加路由時(shí),本質(zhì)上就是根據(jù)用戶的角色信息在asyncRoutes路由配置數(shù)組中進(jìn)行路由篩選,找到相對(duì)應(yīng)的路由,與constantRoutes合并生成最新的路由。
動(dòng)態(tài)添加路由邏輯圖

動(dòng)態(tài)路由源碼分析
代碼入口: 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,具有所有模塊的訪問權(quán)限
accessedRoutes = asyncRoutes || []
} else {
// 如果不是管理員,則需要根據(jù)用戶角色roles和異步路由進(jìn)行篩選
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
// 將最終的結(jié)果存放到vuex中
commit('SET_ROUTES', accessedRoutes)
// resolve出去
resolve(accessedRoutes)
})
}
}
對(duì)異步路由進(jìn)行篩選,并將最終的結(jié)果存放到vuex中,并將結(jié)果resolve出去
export function hasPermission(roles, route) {
if (route.meta && route.meta.roles) { // 如果存在meta.roles
// 只要meta.roles中存在與用戶角色列表中相同的值,則說明具有訪問權(quán)限
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)) { // 相對(duì)路由數(shù)組的每一項(xiàng)進(jìn)行訪問權(quán)限的判斷
if (tmp.children) {
// 如果存在children,則遞歸調(diào)用篩選函數(shù)
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進(jìn)行合并,生成最終的訪問路由
router.addRoutes(accessRoutes)
擴(kuò)展-按鈕權(quán)限
路由權(quán)限控制基本流程已經(jīng)分析完,接下來我們也來看看項(xiàng)目里的按鈕權(quán)限控制的實(shí)現(xiàn),實(shí)現(xiàn)也比較簡單。
基本用法
<div v-permission="['admin','editor']"></div>
import store from '@/store'
function checkPermission(el, binding) {
const { value } = binding
// 從store中拿到我們?cè)L問接口后,取到用戶角色信息
const roles = store.getters && store.getters.roles
if (value && value instanceof Array) { // 判斷傳入的值是不是數(shù)組,規(guī)范化傳值
if (value.length > 0) {
const permissionRoles = value
// 只要傳入的permissionRoles中,包含了roles其中的一個(gè)值即可,則代表有權(quán)限
const hasPermission = roles.some(role => {
return permissionRoles.includes(role)
})
// 沒有權(quán)限則進(jìn)行刪除,不展示。
// v-permission具體實(shí)現(xiàn)可以根據(jù)業(yè)務(wù)場景進(jìn)行修改
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) {
checkPermission(el, binding)
}
}
總結(jié)
存在token
存在用戶角色信息,則說明該用戶的最終可訪問的路由已經(jīng)生成,可以直接放行
不存在用戶信息
1.調(diào)用獲取用戶信息接口,獲取到用戶信息, 將用戶信息存放到vuex中
2.判斷用戶角色
- 如果是管理員則對(duì)所有模塊具有訪問權(quán)限
- 非管理員,需要對(duì)異步路由進(jìn)行篩選,通過遍歷異步路由,并通過meta.roles與用戶信息比較,判斷用戶是否具有訪問權(quán)限
3.將最終的可訪問路由存放到vuex中,最后通過router.addRoutes,整合最后的路由配置列表
不存在token
如果訪問路由在白名單下,則直接進(jìn)行訪問
訪問路由不存在白名單下,則重定向到登錄頁面 path: /login?redirect=/xxx,登錄成功后則跳轉(zhuǎn)到/xxx對(duì)應(yīng)的頁面
以上就是Vue路由權(quán)限控制解析的詳細(xì)內(nèi)容,更多關(guān)于Vue路由權(quán)限控制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue?如何綁定disabled屬性讓其不能被點(diǎn)擊
這篇文章主要介紹了vue?如何綁定disabled屬性讓其不能被點(diǎn)擊,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-04-04
vuejs實(shí)現(xiàn)標(biāo)簽選項(xiàng)卡動(dòng)態(tài)更改css樣式的方法
這篇文章主要介紹了vuejs實(shí)現(xiàn)標(biāo)簽選項(xiàng)卡-動(dòng)態(tài)更改css樣式的方法,代碼分為html和js兩部分,需要的朋友可以參考下2018-05-05
vue+elementui實(shí)現(xiàn)選項(xiàng)卡功能
這篇文章主要為大家詳細(xì)介紹了vue+elementui實(shí)現(xiàn)選項(xiàng)卡功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03
element-ui和vue表單(對(duì)話框)驗(yàn)證提示語(殘留)清除操作
這篇文章主要介紹了element-ui和vue表單(對(duì)話框)驗(yàn)證提示語(殘留)清除操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09
vue3中unplugin-auto-import自動(dòng)引入示例代碼
unplugin-auto-import 這個(gè)插件是為了解決在開發(fā)中的導(dǎo)入問題,下面這篇文章主要給大家介紹了關(guān)于vue3中unplugin-auto-import自動(dòng)引入的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
Vue2.0+Vux搭建一個(gè)完整的移動(dòng)webApp項(xiàng)目的示例
這篇文章主要介紹了Vue2.0+Vux搭建一個(gè)完整的移動(dòng)webApp項(xiàng)目的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-03-03
詳解vue.js2.0父組件點(diǎn)擊觸發(fā)子組件方法
本篇文章主要介紹了詳解vue.js2.0父組件點(diǎn)擊觸發(fā)子組件方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05

