Vue 的本身是沒有權限的,雖然 vue router 本身建議基於 路由把資料塞到 meta 內,不過這個邏輯不方便應用我的後台管理。
目前設計的是公司內部使用的管理介面,所以是有點類似論壇的概念,每個使用者一定會有特定的權限,後端也會設計角色群組,授權給不同的使用者,所以一定會有交錯的權限設計。
目前後端採用的權限控管使用 casbin 權限控管,帶入 laravel 有的 route name,每個路由都會有獨立的 key name ,前端的路由也可以對應後端的路由,不過要確定一個明確的 keyname,根據 laravel 的 resource 定義,能夠讀取首頁的 route name 都定義為 index 。
所以只要有權限進入這個頁面的 .index 都有權限,再者就是字串拆解了,把每個 main route 跟 action method 拆開,就可以得到一個完整個資源,下列是後端的 router 範例:

可以看到右邊是 route name 中間是實際的 route 路徑,舉例 role 拆分下來就是 backend.role 這個 應用 page 有 index、store、assign、show、update、destory 這幾個方法,因此可以在後端確認這些是需要控管的權限,前端只需要確定當 使用者擁有 backend.role.index 這隻權限就可以進入管理 role 的頁面,其餘方法可以用作判斷按鈕,或是操作是否需要顯示的判斷,當然也可以略過這些交由後端判斷。
根據這個邏輯,還需要組成一張 menu 表,因為我把 權限打成 function ,上述的 functions 就是我的 menu 組成,當然你也可以獨立控制。
在這邊建議,建立起後端的 menu 表之後,也不要打成樹狀結構回前端,因為這樣會很難拆分。
前端跟後端要到 menu 的清單,會有兩種方式組成樹狀,傳址或是傳值,傳值對後端的數據要求很絕對,傳值就還好一點。建議前端選一個操作順手的方式將扁平陣列轉為樹狀結構,開始產生側邊選單。
{
const routeMap = {}
const rootList = []
routes.forEach((node) => {
const item = _.cloneDeep(node)
item.checkPermission = () => {
if (item.permission == null) {
return true
} else {
let readPermission = `${item.permission}.index`
return state.permission.get(readPermission)
}
}
item.hasIcon = item.icon !== '' && item.icon !== null
item.hasChildren = false
item.isShow = node.is_show
delete item.is_show
if (item.parent_code === null) {
rootList.push(item.combine_code)
routeMap[item.combine_code] = item
} else {
if (_.isPlainObject(routeMap[item.parent_code])) {
routeMap[item.combine_code] = item
if (routeMap[item.parent_code].children === undefined) {
routeMap[item.parent_code]['children'] = []
routeMap[item.parent_code].hasChildren = true
}
routeMap[item.parent_code].children.push(routeMap[item.combine_code])
}
}
state.routeMapping[item.combine_code] = item
})
state.rootList = rootList
state.routeMap = routeMap
}
我這邊用的是主key 值是 combine_code ,以文字的模式當成 ID,前端採用傳址的模式產生樹狀結構,並且用一個 routeMap 去指定 combine_code = item ,rootList 去指定第一層有哪些 combine_code ,combine_code 100% 對應 前端 router.js 裡面的 list name。
這輸出有個重點是 item.checkPermission() 指定了一個 function,主要是在 RouterBeforeEach 的時候,必須要判斷權限,在vuex 裡面設定了 Action Function:checkPermission,由這支 function 判斷是不是要執行 驗證權限,因為還需要略過白名單,所以就在這邊呼叫了自己本身的權限。
Action Function 如下
import systemRoutes from 'src/router/routes'
//驗證權限,先驗證是否在白名單內,接著驗證後端取回來的選單
checkPermission({ commit, state }, name) {
const route = systemRoutes.find((item) => item.name === name)
const routeItem = state.routeMapping[name]
let access = false
if (route && route.meta && route.meta.whitelist) {
access = true
} else {
access = routeItem ? routeItem.checkPermission() : false
}
return new Promise((resolve, reject) => {
if (access) {
resolve()
} else {
reject()
}
})
}
這樣在運作 router 的時候,就可以呼叫 vuex 的 action 來驗證權限,這樣一來既可以讓 webpack 打包 components,又可以驗證權限進入路由,動態 import component 實在不是我這個初學 vue 的新手來操作,所以還是回歸到程式面來驗證,會來的簡單許多,即使有辦法騙過前端進去需要授權的畫面,後端的 API 控制好權限,有些東西也難以取得。
這邊有個小問題存在,就是權限的父層,在後端的 route 表是不存在的,我的解決辦法是直接建立一個空的資料,設定一個空的 index 來授權,可以解決掉一個應用程式沒有父層這個問題。如果想要製作子層也一樣喔