vue element後臺鑒權流程分析

前言:

最近項目遇到一個管理系統,感覺權限配置挺有意思,記錄一下流程實現的過程,便於自己學習以及整理思路,部分思路整合在代碼的註釋中:

路由攔截鑒權常用的兩種方法

1:路由攔截:單純給路由加字段標識符,通過路由攔截實現
2:動態路由:第二種是通過路由的拆分另外需要後端的配合去實現的動態路由配置

比較:

路由攔截實現方式比較簡單,隻需要簡單的在router.beforeEach中根據路由配置信息過濾頁面是否有權限前往改組件,若相對於的權限不夠則不前往相應的組件

動態路由實現相對比較復雜,並且需要後端的配合,本質是路由配置表分成兩部分,相應的不同用戶登錄的時候,是根據用戶權限信息過濾篩選除路由配置表,動態添加,而用戶沒有權限的部分則不渲染,更適合相對比較大型的後臺系統

註:本篇內容主要介紹動態路由鑒權實現方式

與動態路由相關的通常有以下幾個文件:

  • router.js
  • permission.js(全局的路由攔截文件)
  • store.js

router.js

router.js的路由配置表可以分為兩部分,公共路由以及動態權限路由,動態權限路由可以放在前端,鑒權的時候前端自己進行數組的過濾,也可以放在後端過濾,思路相同,下面介紹的是配置表都放在前端的

export default new  Router({ 
routes:[
   {
		path:'/login',
		name:'login',
		component:aa
	},
	{
		path:'/home',
		name:'home',
		component:cc
	},
	]
})

上面這個是一般項目的路由配置,現在我們需要做鑒權所以需要把路由配置表稍微拆分一下,拆成以下兩個數組:

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

export const defauleRoute = [ //固定部分權限的數組,所有用戶都能訪問的路由
	{
		path:'/login',
		component:aa
	},
]

export const  asyncRoute = [ //動態配置的路由表,工作之前需要過濾
	{
		path:'/order',
		name:'order',
		component:aa,
		meta:{
			system:'order'
		}
	}
	{
		path:'/roles',
		name:'roles',
		component:aa,
		meta:{
			system:'roles'
		}
	}
]

//註冊路由工作表
const createRouter = () => new Router({
	  // mode: 'history', // require service support
	  scrollBehavior: () => ({ y: 0 }),
	  routes: constantRoutes
})
const router = createRouter()

//重置路由工作表,退出登錄的時候需要調用此方法
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher 
}

export default router

permission.js permission文件主要做全局的路由攔截,以及路由根據用戶權限動態過濾等功能,那麼這部分內容主要設涉及的就是兩個問題 什麼時候去處理動態路由什麼條件去處理路由

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"){
			/**
			*用戶已經登陸,但是還路由到login頁面,這代表用戶已經執行瞭退出登錄的操
			*作,所以這個地方可以清一下token之類的,或者自定義寫一些邏輯
			*/
			next()
		}else{
		/**
		*這裡是已經登錄或者點擊瞭登錄按鈕,token已經存入localstorage,但是但是不路
		*由到login的情況如果沒有路由到/login,那麼就直接讓他放行就行,在這裡面我處
		*理一些東西,就是用戶既然已經登陸,並且可以直接放行,那麼我們放
		*行之前,在這個地方需要做一些邏輯,就是判斷用戶的權限,然後根
		*據用戶的權限,把我們的動態配置的路由表中符合他權限的那幾條路
		*由給過濾出來,然後插入到路由配置表中去使用
		*
		*那麼就涉及到兩個問題:
		*1:什麼時候去處理動態路由(登陸之後,進入到首頁之前,也就
		*是next之前)
		*2:什麼條件處理動態路由
		*/
		
		/**
		*這地方可以先判斷一下store中的用戶權限列表長度是否為0,若長度為0,則代表用戶
		*是剛點擊瞭登錄按鈕,但是還沒有進入到頁面,這時候需要再去做一些權限過濾之類的	
		*操作如果長度不為0代表鑒權流程都沒問題瞭,直接前往對應的組件就行
		*這一步主要是為瞭防止重復過濾數組,節約性能
		*/
			if(store.getters.roles.length > 0){
				next()
			}else{
				//代碼如果走到瞭這個地方,代表用戶是已登錄。並且鑒權流程還沒走,
				//那麼在這地方就需要去走鑒權流程
				store.dispatch('getRoles').then(res=>{
					//這個地方的res是第三步那個地方的peomise中的resolve傳
					//過來的,也就是權限信息的那個數組
					store.dispatch('createRouters',res.data)
					.then(res=>{
						//這裡是調用store創造動態路由的那個函數,這個地方可以把那
						//個權限數組傳到這個函數內部,或者不在這裡傳,這個
						//函數內部直接去取自己state裡面的roles的值也是一樣的
						let  addRouters = store.getters('addRouters')
						let  allRouters = store.getters('allRouters')

						//添加動態路由部分到工作路由
						router.addRoutes(accessRoutes)
						//前往攔截的頁面
          				next({ ...to, replace: true })
					})	
				})
			}
		}
	} else {
		/**這裡是處理沒有token的情況,也就是說這時候用戶還沒有登陸,那
		*如果沒用戶登錄,那麼判斷用戶是不是去登錄頁面,如果是登錄
		*頁面,就直接放行,如果沒登陸就想去訪問主頁那種頁面,就讓
		*他重定向到登錄頁面
		*/
		if(to.path == '/login'){
			//這地方可以判斷的仔細一點,不一定是去login的時候再讓他直接放行,而是
			//前往所有公共組件的時候,都直接讓他放行
			next()
		}else{
			next('/login')
		}
	}
})

 

store.js

//在api文件夾中定義一個獲取此用戶的權限的接口,並且在這個actions中調用
import { getUserRole } from "../api/getRoles"  //獲取權限的接口
import { logout } from '../api/user'   //用戶退出登錄的接口
import { resetRouter } from './router'
import { removeToken } from '@/utils/auth' // 自定義封裝清除token的方法

//這個是過濾數組的方法,如果路由表是多層嵌套的,那麼可以遞歸調用這個方法去過濾數組
//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
//}

//引入默認路由以及動態路由
import  { defauleRoute , asyncRouter }  from '@/router'
const state = {
	roles:[]    //掉接口拿到的權限列表,假設數據格式為:["order","roles"],
	allRouters: [], //這個是全部整合以後,最終要工作的路由
	addRouters: [],//這個是根據權限動態匹配過濾出來部分的路由
}
	  
const getters = {
	/**把state中的roles存入到這個getters中,那麼其他獲取這個getters中的
	*roles的地方,隻要原本的roles發生改變,其他地方的這個roles也就會發生
	*改變瞭,這個相當於是computed計算屬性
	*/
	roles:state => state.roles
	allRouters:state => state.allRouters
	addRouters:state => state.addRouters
}
const mutations:{
	/**在下面的actions裡面通過commit把權限信息的數組提交到這個地方,然後
	*這個地方把數組提交到state的roles
	*/
	SetRoute(state,router)
		//這個地方的router就是根據用戶權限,過濾出來的路由表
		state.allRouters = defauleRoute.concat(router)
		state.addRouters = router
	}
	//把路由權限數組存儲到state
	setRoles(state,value){
		state.roles = value
	}
}
const actions:{
	//寫一個獲取當前登陸角色權限的請求,比如["/order","roles"],如果請求回
	//來的是這樣的,那麼就代表這個角色的權限就是可以訪問 order路由以及
	//roles路由
	
	//獲取權限信息可能有兩種情況:除瞭下面這種權限信息是一個單獨的接口,
	//權限信息也可能跟著用戶登陸的接口就一並返回
	//獲取當前用戶的權限信息,並且存入到state中,這個權限信息,可能跟後
	//端在溝通的時候,他不會單獨寫成一個接口給你去請求,而是你在登陸請求
	//的時候就把用戶信息和這個此用戶的權限信息都一次性返回給你瞭,那就在
	//用戶登陸的時候就把這個權限信息存入到這個state中,也一
	//樣的,目的就是要把權限信息的數組存入到state中就行
	//獲取roles權限方法
	getRoles({commit},data){
		return new Promise(resolve,reject){
			//調用獲取用戶權限接口
			getUserRole().then(res =>{
				//這裡返回的數據應該是一個權限信息的數組,如:["order","roles"]
				//把權限信息通過mutations存入到state
				commit('setRoles',res.data)
				resolve(res.data)
			})
		}
	})
	//根據權限過濾數組配置表的方法
	createRouters({ commit } , data ){
		return new Promise((resolve,reject) =>{
			let addRouters =  [ ] 
			if(data.includes("admin"){
				addRouters = asyncRouter
			}else{
				//項目開發中路由數組可能是多層嵌套,那麼這地方需要用上面自定義的方	
				//法通過遞歸的方式去過濾,此demo就隻按一層數組處理
				//(filterAsyncRoutes)方法
				addRouters = asyncRouter.filter(item=>{
					if(data.includes(item.meta.system) ){
					   	return item
					}
				})
			}
			
			
			//把這個匹配出來的權限路由傳到mutations中,讓mutations
			//把這個匹配出來的路由存入到state
			commit.("SetRoute",addRouters)
			resolve()  //這個地方要調用一下這個resolve,這樣外面訪可以通過
					   //.then拿到數組過濾成功的回調
		})
	},
	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{
		//退出登錄失敗(出錯瞭)
	}
}

結尾:

代碼一大堆,其實思路很簡單,不過是拿到路由配置表,過濾數組,動態添加而已

項目參考github:vue-element-admin

到此這篇關於vue element後臺鑒權流程分析的文章就介紹到這瞭,更多相關vue element鑒權內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀:

    None Found