前言:
最近專案遇到一個管理系統,感覺權限配置挺有意思,記錄一下流程實作的程序,便于自己學習以及整理思路,部分思路整合在代碼的注釋中:
路由攔截鑒權常用的兩種方法
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
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/274102.html
標籤:其他
上一篇:初識C語言
