vue element后臺(tái)鑒權(quán)流程分析
前言:
最近項(xiàng)目遇到一個(gè)管理系統(tǒng),感覺權(quán)限配置挺有意思,記錄一下流程實(shí)現(xiàn)的過程,便于自己學(xué)習(xí)以及整理思路,部分思路整合在代碼的注釋中:
路由攔截鑒權(quán)常用的兩種方法
1:路由攔截:?jiǎn)渭兘o路由加字段標(biāo)識(shí)符,通過路由攔截實(shí)現(xiàn)
2:動(dòng)態(tài)路由:第二種是通過路由的拆分另外需要后端的配合去實(shí)現(xiàn)的動(dòng)態(tài)路由配置
比較:
路由攔截實(shí)現(xiàn)方式比較簡(jiǎn)單,只需要簡(jiǎn)單的在router.beforeEach中根據(jù)路由配置信息過濾頁面是否有權(quán)限前往改組件,若相對(duì)于的權(quán)限不夠則不前往相應(yīng)的組件
動(dòng)態(tài)路由實(shí)現(xiàn)相對(duì)比較復(fù)雜,并且需要后端的配合,本質(zhì)是路由配置表分成兩部分,相應(yīng)的不同用戶登錄的時(shí)候,是根據(jù)用戶權(quán)限信息過濾篩選除路由配置表,動(dòng)態(tài)添加,而用戶沒有權(quán)限的部分則不渲染,更適合相對(duì)比較大型的后臺(tái)系統(tǒng)
注:本篇內(nèi)容主要介紹動(dòng)態(tài)路由鑒權(quán)實(shí)現(xiàn)方式
與動(dòng)態(tài)路由相關(guān)的通常有以下幾個(gè)文件:
- router.js
- permission.js(全局的路由攔截文件)
- store.js
router.js
router.js的路由配置表可以分為兩部分,公共路由以及動(dòng)態(tài)權(quán)限路由,動(dòng)態(tài)權(quán)限路由可以放在前端,鑒權(quán)的時(shí)候前端自己進(jìn)行數(shù)組的過濾,也可以放在后端過濾,思路相同,下面介紹的是配置表都放在前端的
export default new Router({ routes:[ { path:'/login', name:'login', component:aa }, { path:'/home', name:'home', component:cc }, ] })
上面這個(gè)是一般項(xiàng)目的路由配置,現(xiàn)在我們需要做鑒權(quán)所以需要把路由配置表稍微拆分一下,拆成以下兩個(gè)數(shù)組:
import Vue from 'vue' import Router from 'vue-router' Vue.use(Router) export const defauleRoute = [ //固定部分權(quán)限的數(shù)組,所有用戶都能訪問的路由 { path:'/login', component:aa }, ] export const asyncRoute = [ //動(dòng)態(tài)配置的路由表,工作之前需要過濾 { path:'/order', name:'order', component:aa, meta:{ system:'order' } } { path:'/roles', name:'roles', component:aa, meta:{ system:'roles' } } ] //注冊(cè)路由工作表 const createRouter = () => new Router({ // mode: 'history', // require service support scrollBehavior: () => ({ y: 0 }), routes: constantRoutes }) const router = createRouter() //重置路由工作表,退出登錄的時(shí)候需要調(diào)用此方法 export function resetRouter() { const newRouter = createRouter() router.matcher = newRouter.matcher } export default router
permission.js permission文件主要做全局的路由攔截,以及路由根據(jù)用戶權(quán)限動(dòng)態(tài)過濾等功能,那么這部分內(nèi)容主要設(shè)涉及的就是兩個(gè)問題 什么時(shí)候去處理動(dòng)態(tài)路由什么條件去處理路由
import router from './router' import store from './store' import { getToken } from '@/utils/auth' // 自定義封裝存取token的方法 Route.beforeEach((to,from,next) =>{ //取token,判斷用戶是否已登錄 const hasToken = getToken() if(hasToken ){ //判斷用戶已登錄 if(to.path === "/login"){ /** *用戶已經(jīng)登陸,但是還路由到login頁面,這代表用戶已經(jīng)執(zhí)行了退出登錄的操 *作,所以這個(gè)地方可以清一下token之類的,或者自定義寫一些邏輯 */ next() }else{ /** *這里是已經(jīng)登錄或者點(diǎn)擊了登錄按鈕,token已經(jīng)存入localstorage,但是但是不路 *由到login的情況如果沒有路由到/login,那么就直接讓他放行就行,在這里面我處 *理一些東西,就是用戶既然已經(jīng)登陸,并且可以直接放行,那么我們放 *行之前,在這個(gè)地方需要做一些邏輯,就是判斷用戶的權(quán)限,然后根 *據(jù)用戶的權(quán)限,把我們的動(dòng)態(tài)配置的路由表中符合他權(quán)限的那幾條路 *由給過濾出來,然后插入到路由配置表中去使用 * *那么就涉及到兩個(gè)問題: *1:什么時(shí)候去處理動(dòng)態(tài)路由(登陸之后,進(jìn)入到首頁之前,也就 *是next之前) *2:什么條件處理動(dòng)態(tài)路由 */ /** *這地方可以先判斷一下store中的用戶權(quán)限列表長(zhǎng)度是否為0,若長(zhǎng)度為0,則代表用戶 *是剛點(diǎn)擊了登錄按鈕,但是還沒有進(jìn)入到頁面,這時(shí)候需要再去做一些權(quán)限過濾之類的 *操作如果長(zhǎng)度不為0代表鑒權(quán)流程都沒問題了,直接前往對(duì)應(yīng)的組件就行 *這一步主要是為了防止重復(fù)過濾數(shù)組,節(jié)約性能 */ if(store.getters.roles.length > 0){ next() }else{ //代碼如果走到了這個(gè)地方,代表用戶是已登錄。并且鑒權(quán)流程還沒走, //那么在這地方就需要去走鑒權(quán)流程 store.dispatch('getRoles').then(res=>{ //這個(gè)地方的res是第三步那個(gè)地方的peomise中的resolve傳 //過來的,也就是權(quán)限信息的那個(gè)數(shù)組 store.dispatch('createRouters',res.data) .then(res=>{ //這里是調(diào)用store創(chuàng)造動(dòng)態(tài)路由的那個(gè)函數(shù),這個(gè)地方可以把那 //個(gè)權(quán)限數(shù)組傳到這個(gè)函數(shù)內(nèi)部,或者不在這里傳,這個(gè) //函數(shù)內(nèi)部直接去取自己state里面的roles的值也是一樣的 let addRouters = store.getters('addRouters') let allRouters = store.getters('allRouters') //添加動(dòng)態(tài)路由部分到工作路由 router.addRoutes(accessRoutes) //前往攔截的頁面 next({ ...to, replace: true }) }) }) } } } else { /**這里是處理沒有token的情況,也就是說這時(shí)候用戶還沒有登陸,那 *如果沒用戶登錄,那么判斷用戶是不是去登錄頁面,如果是登錄 *頁面,就直接放行,如果沒登陸就想去訪問主頁那種頁面,就讓 *他重定向到登錄頁面 */ if(to.path == '/login'){ //這地方可以判斷的仔細(xì)一點(diǎn),不一定是去login的時(shí)候再讓他直接放行,而是 //前往所有公共組件的時(shí)候,都直接讓他放行 next() }else{ next('/login') } } })
store.js
//在api文件夾中定義一個(gè)獲取此用戶的權(quán)限的接口,并且在這個(gè)actions中調(diào)用 import { getUserRole } from "../api/getRoles" //獲取權(quán)限的接口 import { logout } from '../api/user' //用戶退出登錄的接口 import { resetRouter } from './router' import { removeToken } from '@/utils/auth' // 自定義封裝清除token的方法 //這個(gè)是過濾數(shù)組的方法,如果路由表是多層嵌套的,那么可以遞歸調(diào)用這個(gè)方法去過濾數(shù)組 //function hasPermission(roles, route) { // if (route.meta && route.meta.roles) { // return roles.some(role => route.meta.roles.includes(role)) // } else { // return true // } //} //export function filterAsyncRoutes(routes, roles) { // const res = [] // routes.forEach(route => { // const tmp = { ...route } // if (hasPermission(roles, tmp)) { // if (tmp.children) { // tmp.children = filterAsyncRoutes(tmp.children, roles) // } // res.push(tmp) // } // }) // // return res //} //引入默認(rèn)路由以及動(dòng)態(tài)路由 import { defauleRoute , asyncRouter } from '@/router' const state = { roles:[] //掉接口拿到的權(quán)限列表,假設(shè)數(shù)據(jù)格式為:["order","roles"], allRouters: [], //這個(gè)是全部整合以后,最終要工作的路由 addRouters: [],//這個(gè)是根據(jù)權(quán)限動(dòng)態(tài)匹配過濾出來部分的路由 } const getters = { /**把state中的roles存入到這個(gè)getters中,那么其他獲取這個(gè)getters中的 *roles的地方,只要原本的roles發(fā)生改變,其他地方的這個(gè)roles也就會(huì)發(fā)生 *改變了,這個(gè)相當(dāng)于是computed計(jì)算屬性 */ roles:state => state.roles allRouters:state => state.allRouters addRouters:state => state.addRouters } const mutations:{ /**在下面的actions里面通過commit把權(quán)限信息的數(shù)組提交到這個(gè)地方,然后 *這個(gè)地方把數(shù)組提交到state的roles */ SetRoute(state,router) //這個(gè)地方的router就是根據(jù)用戶權(quán)限,過濾出來的路由表 state.allRouters = defauleRoute.concat(router) state.addRouters = router } //把路由權(quán)限數(shù)組存儲(chǔ)到state setRoles(state,value){ state.roles = value } } const actions:{ //寫一個(gè)獲取當(dāng)前登陸角色權(quán)限的請(qǐng)求,比如["/order","roles"],如果請(qǐng)求回 //來的是這樣的,那么就代表這個(gè)角色的權(quán)限就是可以訪問 order路由以及 //roles路由 //獲取權(quán)限信息可能有兩種情況:除了下面這種權(quán)限信息是一個(gè)單獨(dú)的接口, //權(quán)限信息也可能跟著用戶登陸的接口就一并返回 //獲取當(dāng)前用戶的權(quán)限信息,并且存入到state中,這個(gè)權(quán)限信息,可能跟后 //端在溝通的時(shí)候,他不會(huì)單獨(dú)寫成一個(gè)接口給你去請(qǐng)求,而是你在登陸請(qǐng)求 //的時(shí)候就把用戶信息和這個(gè)此用戶的權(quán)限信息都一次性返回給你了,那就在 //用戶登陸的時(shí)候就把這個(gè)權(quán)限信息存入到這個(gè)state中,也一 //樣的,目的就是要把權(quán)限信息的數(shù)組存入到state中就行 //獲取roles權(quán)限方法 getRoles({commit},data){ return new Promise(resolve,reject){ //調(diào)用獲取用戶權(quán)限接口 getUserRole().then(res =>{ //這里返回的數(shù)據(jù)應(yīng)該是一個(gè)權(quán)限信息的數(shù)組,如:["order","roles"] //把權(quán)限信息通過mutations存入到state commit('setRoles',res.data) resolve(res.data) }) } }) //根據(jù)權(quán)限過濾數(shù)組配置表的方法 createRouters({ commit } , data ){ return new Promise((resolve,reject) =>{ let addRouters = [ ] if(data.includes("admin"){ addRouters = asyncRouter }else{ //項(xiàng)目開發(fā)中路由數(shù)組可能是多層嵌套,那么這地方需要用上面自定義的方 //法通過遞歸的方式去過濾,此demo就只按一層數(shù)組處理 //(filterAsyncRoutes)方法 addRouters = asyncRouter.filter(item=>{ if(data.includes(item.meta.system) ){ return item } }) } //把這個(gè)匹配出來的權(quán)限路由傳到mutations中,讓mutations //把這個(gè)匹配出來的路由存入到state commit.("SetRoute",addRouters) resolve() //這個(gè)地方要調(diào)用一下這個(gè)resolve,這樣外面訪可以通過 //.then拿到數(shù)組過濾成功的回調(diào) }) }, logout({ commit }) { return new Promise((resolve, reject) => { logout().then(() => { removeToken() // must remove token first resetRouter() commit('setRoles', []) commit('SetRoute', []) resolve() }).catch(error => { reject(error) }) }) }, } export default { state, getters, mutations, actions }
退出登錄:
async function logout(){ try{ const res = await store.dispatch.logout() if(res.code == 200){ //退出登錄成功 } }catch{ //退出登錄失?。ǔ鲥e(cuò)了) } }
結(jié)尾:
代碼一大堆,其實(shí)思路很簡(jiǎn)單,不過是拿到路由配置表,過濾數(shù)組,動(dòng)態(tài)添加而已
項(xiàng)目參考github:vue-element-admin
到此這篇關(guān)于vue element后臺(tái)鑒權(quán)流程分析的文章就介紹到這了,更多相關(guān)vue element鑒權(quán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- vue+elementUI組件遞歸實(shí)現(xiàn)可折疊動(dòng)態(tài)渲染多級(jí)側(cè)邊欄導(dǎo)航
- vue + element ui實(shí)現(xiàn)播放器功能的實(shí)例代碼
- vue基于Element按鈕權(quán)限實(shí)現(xiàn)方案
- vue+element+oss實(shí)現(xiàn)前端分片上傳和斷點(diǎn)續(xù)傳
- 解決element DateTimePicker+vue彈出框只顯示小時(shí)
- Vue模仿ElementUI的form表單實(shí)例代碼
- 使用Vue實(shí)現(xiàn)一個(gè)樹組件的示例
- vue+iview使用樹形控件的具體使用
- vue中可編輯樹狀表格的實(shí)現(xiàn)代碼
- Vue Element前端應(yīng)用開發(fā)之樹列表組件
相關(guān)文章
詳解vue-cli 本地開發(fā)mock數(shù)據(jù)使用方法
這篇文章主要介紹了詳解vue-cli 本地開發(fā)mock數(shù)據(jù)使用方法,如果后端接口尚未開發(fā)完成,前端開發(fā)一般使用mock數(shù)據(jù)。小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05element-plus一個(gè)vue3.xUI框架(element-ui的3.x 版初體驗(yàn))
這篇文章主要介紹了element-plus一個(gè)vue3.xUI框架(element-ui的3.x 版初體驗(yàn)),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12詳解nginx配置vue h5 history去除#號(hào)
這篇文章主要介紹了詳解nginx配置vue h5 history去除#號(hào),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11解決Vuepress碼云部署及自動(dòng)跳轉(zhuǎn)404的問題
這篇文章主要介紹了解決Vuepress碼云部署及自動(dòng)跳轉(zhuǎn)404的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09在Vue中使用Viser說明(基于AntV-G2可視化引擎)
這篇文章主要介紹了在Vue中使用Viser說明(基于AntV-G2可視化引擎),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10