傳統許可權管理:
類似於這樣,每新增一個人都要重新給她一些許可權,是針對每個人單獨設定的,這種方法已經不適用於高效管控許可權的
基於此,RBAC許可權模型就誕生了,Role-Based Access control也就是基於角色的許可權控制,相對於傳統模式,這套方案強調一個role角色
RBAC實現了使用者和許可權點的分離,相對某個使用者設定許可權,不用再去一個許可權一個許可權的給他單獨設定,而是直接給他設定角色即可,這樣許可權的分配和設計就達到了極簡,高效,當想對使用者收回許可權時,只需要收回角色即可
應該在點選角色時彈出角色表單
首先新建一個彈出層dialog的元件
裡面標籤體dialog的一個居中佈局
注意el-row快速變為flex佈局,且居中分佈
然後我們中間的內容部分用到元件 ,核取方塊組checkbox-group,中間是我們後面的角色迴圈每一項
注意這個元件接受兩個父元件傳過來的值,一個是該dialog顯示隱藏的引數,一個是當前點的這個使用者的id等資訊,我拿到了才能去給這個使用者新增角色
首先需要在父元件匯入彈出元件並應用在頁面中
然後我們首先需要傳控制當前彈出顯示隱藏的資料
在父元件data定義好一個變數
給子元件傳過來
注意我們props是多個單詞采用駝峰命名法,也就是props是支援駝峰命名的,但是在用的時候,也就是傳過來的值這裡要採用首字母全小寫,連線線連線的形式
後面加一個.sync的修飾符瞬間形成了自定義事件,方便我們後面子元件點選取消確定等修改他的值
然後我們還要定義一個userId給子元件傳過來
給角色按鈕新增點選事件,傳參當前這一行的id
在這個事件立面展示dialog,將userid賦值即可
然後就應該在子元件這邊來操作了
之前在做這個頁面的時候,獲取過角色列表
按需匯入我們這個請求函數
然後再created勾點發起請求
這裡注意,我們這個介面預設是返回十條資料,我們就傳進去引數20,就假裝預設為有20個角色,一般角色也不會超過很多
然後取裡面的角色列表給到data
遍歷迴圈我們的核取方塊每一項
注意這裡面的label,我們element元件說的很清楚,這裡label既是顯示的值,也充當我們收集的值
但是我這裡只想收集id,顯示name怎麼辦
他存他的,我顯示的內容用一個插值語法來代替即可
然後我們得checkbox-group也要繫結一個陣列用來儲存我們選擇的一些id值
data先定義
然後我們需要將當前使用者已經繫結好的一些角色許可權一開啟就勾選上的狀態
這裡是這樣做的
還是在子元件匯入請求函數,並寫在方法內
但是我們這裡不是在這裡呼叫,是我們父元件一點選角色這個事件裡面來呼叫
這裡不能直接用父元件傳過來的id作為引數
因為我們這個id是props接受的,一個重要概念,props賦值是非同步的,所以我們要一點選角色開啟的瞬間就發起請求,這個時候沒有這個id
我們可以通過給外層函數接受一個引數,然後我們的介面函數拿這個引數作為id,要知道我們是在父元件來呼叫
給子元件打標識拿到範例,然後在點選事件呼叫這個函數
我們每次點開會有一個閃空白的瞬間
解決思路:
其原因是因為我們定義的這個函數因為async和await所以它是一個非同步函數,而我們點選事件裡面,展示為true是同步操作
所以我們應該讓方法呼叫完執行完了再去展示
那麼就可以給我們的這個調取範例的函數呼叫來一個await 等待他執行完才去執行下面的
定義好儲存使用者角色的介面函數
給dialog確定取消點選事件
點選確定給引數放進去,並且sync修飾符的自定義事件傳值關閉彈出層
點選取消直接關閉彈出層
如此就完成了給使用者分配角色,接下來就是給角色分配許可權
在企業服務中許可權一般為:頁面存取許可權 、按鈕操作許可權、api存取許可權(多見於在後端進行攔截),前端一般就前兩個
而且我們一般是先有存取權再有按鈕操作權,連頁面都看不到何談按鈕操作
一般許可權管理頁面都是tree樹形結構,但是除了tree這個元件我們還可以用table元件來做
結構就像這樣一個新增按鈕,一個表格裡面四列
建立一個介面統一管理檔案,這個頁面要發起五個請求,分別是獲取許可權列表,新增許可權、檢視許可權、修改和刪除
呼叫介面請求,賦值data,並繫結到頁面渲染
這樣就渲染上去了,但是隻有一級目錄
我們目前只有一級,但是我們的二級三級都是在這個列表裡面的
可以看到我們的資料結構,type對應層級,二級的pid就是他的父級的id
所以這裡有一個專門的解決思路,針對於我們的list資料沒有children,只有id和pid轉化為樹形結構的封裝函數
第一個引數是我們的列表,第二個引數為我們的根植,也就是pid在一級下的一個值,找到這就說明到頭了
在函數裡面定義一個新陣列,對我們的列表進行迴圈
第一次判斷我們的一級都會判斷成功進來,然後利用遞迴還是list去找,但是根值為我們的當前一級的id,也就是去查詢list裡面pid等於一級id的item
所以我們可以配合這個函數直接將我們的list改變結構
還沒完,我們的table元件確實有樹形結構,他說的很清楚要加一個row-key而且必須為唯一指定值
現在就有結構了
由於我們支援兩層,一個頁面存取權,一個按鈕權
所以這個新增操作,只有一級才可以新增
只需要前四個
用dialog裡面放form
建立好要收集的data
v-model繫結上來
注意一下這裡的switch
按理說他應該是一個Boolean,但是檢視檔案可知,他也可以繫結字串數位等
只不過繫結其他值的時候我們需要設定兩個屬性,讓他知道什麼代表開什麼代表關
底部確定與取消的固定套路寫法
先刪除
通過confirm彈出提示框執行兩個成功回撥,注意returnpromise的結果,可以在後面鏈式程式設計繼續then接受他的成功回撥
新增
我們有兩個新增,上面是新增頁面存取權,下面這個是新增按鈕操作權
給他們都來同一個點選事件回撥開啟剛才的dialog,通過type來判斷是頁面權還是按鈕權,還要傳進來一個id,如果是頁面權直接為0,按鈕權的pid就為當前row的id
然後校驗一些表單
當我們點選確定就應該去儲存發起請求了
首先我們還要校驗一下表單驗證
注意驗證除了平時那種用法,還可以用回撥的用法
校驗成功發起請求,請求成功呼叫message提示
編輯
獲取當前這一行的資料,進行回顯
但是點選確定要修改一下,要判斷是編輯還是新增
直接加一個if判斷有id就是編輯,沒有id就是新增
同樣是dialog,先完成下面確定取消按鈕
上面為一個樹形結構
給分配許可權來一個點選事件,裡面還是通過pid和id轉為tree結構的列表函數來發起請求
但會發現有行數沒有字型
這個時候就要用到我們的tree元件第二個繫結的值,它是用來定義顯示欄位的名稱
將裡面的每一項新增勾選框
以及當我點選他的父級,子級不會預設勾選上,因為我如果只想讓他有頁面權,沒有按鈕權,並不需要勾選父級,子級也能勾選上
點選分配許可權將id儲存下來,不管是看你已經有的許可權點,還是點選確定儲存新增刪除的,都需要他
獲取當前使用者所擁有的許可權點
但是你會發現返回的是一個陣列並且全是這個許可權的id
這個時候tree元件又有兩個屬性
node-key繫結這個樹形的唯一標識
預設勾選的節點陣列
然後給他賦值,並且作為default的值即可
就是當我們點選確定按鈕應該去發起請求
首先要收集我們勾選上的值
在tree有一個方法,返回勾選的節點形成的陣列,又因為我們前面設定了唯一標識為id,所以這個陣列就是id形成的
我們的介面剛好需要許可權點的id和當前這個角色的id
怎麼來呼叫這個方法
可以通過給tree來一個ref來呼叫
這個時候可以完成許可權分配了,但是還有個小bug當我們點選確定後再次點選會發現還是顯示的之氣預設勾選上的,那是因為我們顯示勾選上的這個陣列還沒有清空,並且我們清空是在取消這個函數裡面清空
首先要知道這個取消函數不光是繫結給這個取消按鈕的,還有我們的dialog close事件,也就是右上角的x
所以我們點選確定儲存之後會將show改為false,相當於執行了一下close事件
自此給員工分角色,給角色分配許可權就已經完成,rbac的許可權資料層完成
在我們之前返回的許可權點的資料中可以看到除了name、id、pid之外還有一個屬性 標識
這個標識可以跟我們路由模組相關聯,意思就是該使用者有這個標識就能存取這個路由,沒有就不能存取
用到vue-router提供的一個方法 addRoutes
大體思路如下
在vuex新建一個js模組
在這裡面先完成一個邏輯,匯入我們的常數路由,也就是每個人都擁有的路由比如404、登入、首頁等等
直接讓我們的routes等於常數路由
然後我們要對這個routes做一些操作,也就是讓常數路由➕你自己擁有許可權的路由就等於真正的你能存取到的路由
但是這裡這麼寫會有問題,我們如果用state.routes作為每次的基礎值,那麼如果前面是管理員登入,他擁有100個頁面的許可權,這個時候已經給到了routes,我後面又用另一個人來登入,他就會有管理員的基礎頁面許可權,所以是不對的
應該每次用常數路由來進行一個比較
之前做登入的路由守衛做過這樣一個驗證
當我們有token,頁面不在登入頁要獲取使用者資料之後,這個藏著改使用者所有的許可權標識就在這裡面
所以我們應該在這裡來完成
發現沒有使用者資訊,就去獲取
在這個actions裡面就能拿到我們的標識,怎麼和路由進行比對,我們每個路由都設定的有name,和name比較即可
然後我們需要在剛才的vuex新增的模組來一個actions篩選許可權路由
我們這裡非同步路由是這樣設定的,因為分了模組化
在這個篩選函數裡面第一個引數context,第二個引數就是傳進來的menus,直接對他來一個遍歷,每一個item就是標識
匯入非同步路由,在遍歷裡面讓他所擁有的的每一個許可權的標識去和非同步路由name做一個filter,這個方法會返回一個篩選完的陣列
我們陣列不能push陣列,所以可以先給他解開
然後將篩選出的路由給到mutations,也就是會賦值給state
這裡commit是為了給到state也就是為了左側的選單的一個顯示,而return是為了後面addRoutes這個方法,它是為了我們的url的一個路由顯示
現在回到我們剛才所說的應該在路由守衛的那個位置來dispatch我們的篩選路由
還沒完,我們還要發引數,在我們上面獲取使用者資訊的函數,專門return了一個返回值
拿到返回值,傳引數
這裡為什麼後面這個也要接收返回值
別忘了,我們的這個actions也定義了一個routes的return
這裡有一個注意點,如果這裡呼叫addRoutes那就必須用next(to.path)不能直接用next()
這是vue-router的一個bug
現在url存取自己有許可權的能存取
沒有許可權的為404
但是現在左側選單欄還沒有顯示出來
原因是因為這個模板左側選單遍歷的是路由表中的路由
因為我們的addroutes並不是動態變化的,我們說的commit是給左側選單用的,這時候就發揮用場了
建立一個getters
直接匯入
並且將我們之前的刪除
首先是我們點選退出登入還會有問題,就是這個時候可以看到我們的state裡面的routes還為上一個使用者的許可權路由,並且我在url去輸入對應的路由還可以進入
這是因為我們一直在addRoutes而沒有在登出去重置或者刪除他
在我們的vuerouter檔案裡面可以看到一個函數,這個是重置路由的函數
那麼我們應該在登出的vuex裡面呼叫一下這個方法
然後第二部操作應該將state裡面的routes為初始狀態
我們應該呼叫這個mutations,然後傳進去的引數為空陣列
問題來了,我們這裡是在一個vuex裡面要去commit兄弟級別的vuex裡面這個怎麼來做
子模組呼叫子模組,兩種情況,一種是都沒加鎖的情況都沒有名稱空間的情況,那就可以直接呼叫,因為都預設是在全域性設定下的
第二種是都加了名稱空間的情況,我們的commit包括dispatch其實是有第三個引數的,第三個引數是一個物件,如果來一個root:true,表示呼叫根級的子模組
解決第二個問題
當我們一重新整理就會出現404
看到我們的router組態檔,有一個404的重定向路由,他有一個宣告說的必須放到最後,這裡我們由於把她放進了常數路由,又加了一些vuex裡面的路由再加上非同步路由導致他雖然這裡是在最後,但其實已經沒有在最後的位置了
所以我們應該把這一段刪除,然後新增在我們addRoutes這裡
前面我們完成了頁面的存取權,但是該頁面中某些功能使用者可能有也可能沒有,這就是功能受限
也是在我們userInfo資料裡面的points這個屬性裡面
就是我們許可權管理下頁面許可權下
這就表示有新增的按鈕許可權
就是讓有這個許可權的按鈕顯示,沒有就隱藏
定義規則
全域性混入
然後那個按鈕有許可權就呼叫這個方法,引數為這個按鈕的許可權標識
當然你直接是想連看都看不到