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