RBAC許可權管理

10發表於2022-12-28

一.概述

傳統許可權管理:

image-20221101155238501

類似於這樣,每新增一個人都要重新給她一些許可權,是針對每個人單獨設定的,這種方法已經不適用於高效管控許可權的

基於此,RBAC許可權模型就誕生了,Role-Based Access control也就是基於角色的許可權控制,相對於傳統模式,這套方案強調一個role角色

image-20221101155927200

RBAC實現了使用者和許可權點的分離,相對某個使用者設定許可權,不用再去一個許可權一個許可權的給他單獨設定,而是直接給他設定角色即可,這樣許可權的分配和設計就達到了極簡,高效,當想對使用者收回許可權時,只需要收回角色即可

二.分配員工角色

2.1 新建角色窗體

應該在點選角色時彈出角色窗體

image-20221101161419809

image-20221101161440246

首先新建一個彈出層dialog的元件

image-20221101161634238

裡面標籤體dialog的一個居中佈局

注意el-row快速變為flex佈局,且居中分佈

image-20221101162159147

然後我們中間的內容部分用到元件 ,核取方塊組checkbox-group,中間是我們後面的角色迴圈每一項

image-20221101162347741

注意這個元件接受兩個父元件傳過來的值,一個是該dialog顯示隱藏的引數,一個是當前點的這個使用者的id等資訊,我拿到了才能去給這個使用者新增角色

image-20221101162546788

2.2 獲取角色列表和當前使用者角色

首先需要在父元件匯入彈出元件並應用在頁面中

image-20221101163339564

然後我們首先需要傳控制當前彈出顯示隱藏的資料

在父元件data定義好一個變數

image-20221101163445697

給子元件傳過來

image-20221101163503596

注意我們props是多個單詞采用駝峰命名法,也就是props是支援駝峰命名的,但是在用的時候,也就是傳過來的值這裡要採用首字母全小寫,連線線連線的形式

後面加一個.sync的修飾符瞬間形成了自定義事件,方便我們後面子元件點選取消確定等修改他的值

然後我們還要定義一個userId給子元件傳過來

image-20221101163927740

image-20221101163940171

給角色按鈕新增點選事件,傳參當前這一行的id

在這個事件立面展示dialog,將userid賦值即可

image-20221101164038159

然後就應該在子元件這邊來操作了

之前在做這個頁面的時候,獲取過角色列表

image-20221101171110141

image-20221101171218949

按需匯入我們這個請求函式

image-20221101171322769

然後再created鉤子發起請求

這裡注意,我們這個介面預設是返回十條資料,我們就傳進去引數20,就假裝預設為有20個角色,一般角色也不會超過很多

image-20221101171446325

然後取裡面的角色列表給到data

image-20221101171530834

遍歷迴圈我們的核取方塊每一項

注意這裡面的label,我們element元件說的很清楚,這裡label既是顯示的值,也充當我們收集的值

image-20221101171742225

image-20221101171609914

但是我這裡只想收集id,顯示name怎麼辦

他存他的,我顯示的內容用一個插值語法來代替即可

image-20221101171830181

然後我們得checkbox-group也要繫結一個陣列用來儲存我們選擇的一些id值

data先定義

image-20221101171951672

image-20221101172018627

然後我們需要將當前使用者已經繫結好的一些角色許可權一開啟就勾選上的狀態

image-20221101174239420

這裡是這樣做的

還是在子元件匯入請求函式,並寫在方法內

但是我們這裡不是在這裡呼叫,是我們父元件一點選角色這個事件裡面來呼叫

這裡不能直接用父元件傳過來的id作為引數

image-20221101173349399

因為我們這個id是props接受的,一個重要概念,props賦值是非同步的,所以我們要一點選角色開啟的瞬間就發起請求,這個時候沒有這個id

我們可以透過給外層函式接受一個引數,然後我們的介面函式拿這個引數作為id,要知道我們是在父元件來呼叫

image-20221101173609503

給子元件打標識拿到例項,然後在點選事件呼叫這個函式

image-20221101173645627

我們每次點開會有一個閃空白的瞬間

解決思路:

其原因是因為我們定義的這個函式因為async和await所以它是一個非同步函式,而我們點選事件裡面,展示為true是同步操作

image-20221101174510848

所以我們應該讓方法呼叫完執行完了再去展示

那麼就可以給我們的這個調取例項的函式呼叫來一個await 等待他執行完才去執行下面的

image-20221101174554703

2.3 給員工分配角色

定義好儲存使用者角色的介面函式

image-20221101174809831

給dialog確定取消點選事件

image-20221101174839511

點選確定給引數放進去,並且sync修飾符的自定義事件傳值關閉彈出層

image-20221101175026985

點選取消直接關閉彈出層

如此就完成了給使用者分配角色,接下來就是給角色分配許可權

三.許可權點管理頁面

3.1 新建許可權點管理頁面

在企業服務中許可權一般為:頁面訪問許可權 、按鈕操作許可權、api訪問許可權(多見於在後端進行攔截),前端一般就前兩個

而且我們一般是先有訪問權再有按鈕操作權,連頁面都看不到何談按鈕操作

image-20221102125334509

一般許可權管理頁面都是tree樹形結構,但是除了tree這個元件我們還可以用table元件來做

image-20221102130012706

結構就像這樣一個新增按鈕,一個表格裡面四列

image-20221102130540054

建立一個介面統一管理檔案,這個頁面要發起五個請求,分別是獲取許可權列表,新增許可權、檢視許可權、修改和刪除

image-20221102132947638

3.2 獲取資料轉化樹形

呼叫介面請求,賦值data,並繫結到頁面渲染

image-20221102133336197

這樣就渲染上去了,但是隻有一級目錄

image-20221102133452533

我們目前只有一級,但是我們的二級三級都是在這個列表裡面的

可以看到我們的資料結構,type對應層級,二級的pid就是他的父級的id

image-20221102140552754

所以這裡有一個專門的解決思路,針對於我們的list資料沒有children,只有id和pid轉化為樹形結構的封裝函式

第一個引數是我們的列表,第二個引數為我們的根植,也就是pid在一級下的一個值,找到這就說明到頭了

在函式里面定義一個新陣列,對我們的列表進行迴圈

第一次判斷我們的一級都會判斷成功進來,然後利用遞迴還是list去找,但是根值為我們的當前一級的id,也就是去查詢list裡面pid等於一級id的item

image-20221102135027725

所以我們可以配合這個函式直接將我們的list改變結構

image-20221102143228491

還沒完,我們的table元件確實有樹形結構,他說的很清楚要加一個row-key而且必須為唯一指定值

image-20221102143411047

image-20221102143451159

現在就有結構了

image-20221102143518384

由於我們支援兩層,一個頁面訪問權,一個按鈕權

image-20221102143618004

所以這個新增操作,只有一級才可以新增

image-20221102143741067

3.3 新增編輯許可權彈層

只需要前四個

image-20221102144025706

用dialog裡面放form

image-20221102144542685

建立好要收集的data

image-20221102144610268

v-model繫結上來

image-20221102144636194

注意一下這裡的switch

按理說他應該是一個Boolean,但是檢視文件可知,他也可以繫結字串數字等

image-20221102144728222

只不過繫結其他值的時候我們需要設定兩個屬性,讓他知道什麼代表開什麼代表關

image-20221102144758647

image-20221102144839851

底部確定與取消的固定套路寫法

image-20221102144923862

3.4 新增、編輯、刪除許可權點

先刪除

image-20221102145017441

透過confirm彈出提示框執行兩個成功回撥,注意returnpromise的結果,可以在後面鏈式程式設計繼續then接受他的成功回撥

image-20221102145349458

新增

我們有兩個新增,上面是新增頁面訪問權,下面這個是新增按鈕操作權

image-20221102151028446

給他們都來同一個點選事件回撥開啟剛才的dialog,透過type來判斷是頁面權還是按鈕權,還要傳進來一個id,如果是頁面權直接為0,按鈕權的pid就為當前row的id

image-20221102151530285

然後校驗一些表單

image-20221102151610662

image-20221102151627221

當我們點選確定就應該去儲存發起請求了

首先我們還要校驗一下表單驗證

image-20221102151728137

注意驗證除了平時那種用法,還可以用回撥的用法

校驗成功發起請求,請求成功呼叫message提示

image-20221102151951498

編輯

獲取當前這一行的資料,進行回顯

image-20221102152242368

但是點選確定要修改一下,要判斷是編輯還是新增

直接加一個if判斷有id就是編輯,沒有id就是新增

image-20221102152329497

四.給角色分配許可權

4.1 分配許可權彈出層

image-20221102161437617

同樣是dialog,先完成下面確定取消按鈕

image-20221102161838342

上面為一個樹形結構

image-20221102162404123

給分配許可權來一個點選事件,裡面還是透過pid和id轉為tree結構的列表函式來發起請求

image-20221102162712229

但會發現有行數沒有字型

image-20221102162557826

這個時候就要用到我們的tree元件第二個繫結的值,它是用來定義顯示欄位的名稱

image-20221102162810158

將裡面的每一項新增勾選框

image-20221102163012567

以及當我點選他的父級,子級不會預設勾選上,因為我如果只想讓他有頁面權,沒有按鈕權,並不需要勾選父級,子級也能勾選上

image-20221102163133366

image-20221102163146307

點選分配許可權將id儲存下來,不管是看你已經有的許可權點,還是點選確定儲存新增刪除的,都需要他

image-20221102163358229

獲取當前使用者所擁有的許可權點

image-20221102163651392

但是你會發現返回的是一個陣列並且全是這個許可權的id

這個時候tree元件又有兩個屬性

node-key繫結這個樹形的唯一標識

image-20221102163747866

預設勾選的節點陣列

image-20221102163847825

然後給他賦值,並且作為default的值即可

image-20221102163923108

image-20221102163933805

4.2 給角色分配許可權

就是當我們點選確定按鈕應該去發起請求

首先要收集我們勾選上的值

在tree有一個方法,返回勾選的節點形成的陣列,又因為我們前面設定了唯一標識為id,所以這個陣列就是id形成的

image-20221102165310428

我們的介面剛好需要許可權點的id和當前這個角色的id

怎麼來呼叫這個方法

可以透過給tree來一個ref來呼叫

image-20221102165438980

這個時候可以完成許可權分配了,但是還有個小bug當我們點選確定後再次點選會發現還是顯示的之氣預設勾選上的,那是因為我們顯示勾選上的這個陣列還沒有清空,並且我們清空是在取消這個函式里面清空

image-20221102165824214

首先要知道這個取消函式不光是繫結給這個取消按鈕的,還有我們的dialog close事件,也就是右上角的x

image-20221102165920517

所以我們點選確定儲存之後會將show改為false,相當於執行了一下close事件

自此給員工分角色,給角色分配許可權就已經完成,rbac的許可權資料層完成

五.前端許可權應用-頁面訪問和選單

5.1 主體思路

在我們之前返回的許可權點的資料中可以看到除了name、id、pid之外還有一個屬性 標識

這個標識可以跟我們路由模組相關聯,意思就是該使用者有這個標識就能訪問這個路由,沒有就不能訪問

image-20221102170942268

用到vue-router提供的一個方法 addRoutes

大體思路如下

image-20221102170923213

5.2 新建vuex管理許可權模組

在vuex新建一個js模組

image-20221102171316842

在這裡面先完成一個邏輯,匯入我們的常量路由,也就是每個人都擁有的路由比如404、登入、首頁等等

直接讓我們的routes等於常量路由

然後我們要對這個routes做一些操作,也就是讓常量路由➕你自己擁有許可權的路由就等於真正的你能訪問到的路由

image-20221102172055117

但是這裡這麼寫會有問題,我們如果用state.routes作為每次的基礎值,那麼如果前面是管理員登入,他擁有100個頁面的許可權,這個時候已經給到了routes,我後面又用另一個人來登入,他就會有管理員的基礎頁面許可權,所以是不對的

image-20221102172321047

應該每次用常量路由來進行一個比較

image-20221102172349409

5.3 vuex篩選許可權路由

之前做登入的路由守衛做過這樣一個驗證

image-20221102175021739

當我們有token,頁面不在登入頁要獲取使用者資料之後,這個藏著改使用者所有的許可權標識就在這裡面

所以我們應該在這裡來完成

image-20221102175402184

發現沒有使用者資訊,就去獲取

image-20221102175457860

image-20221102175833073

在這個actions裡面就能拿到我們的標識,怎麼和路由進行比對,我們每個路由都設定的有name,和name比較即可

image-20221102175636260

然後我們需要在剛才的vuex新增的模組來一個actions篩選許可權路由

我們這裡非同步路由是這樣設定的,因為分了模組化

image-20221102175919562

image-20221102175927514

在這個篩選函式里面第一個引數context,第二個引數就是傳進來的menus,直接對他來一個遍歷,每一個item就是標識

image-20221102180022614

匯入非同步路由,在遍歷裡面讓他所擁有的的每一個許可權的標識去和非同步路由name做一個filter,這個方法會返回一個篩選完的陣列

我們陣列不能push陣列,所以可以先給他解開

image-20221102180156639

然後將篩選出的路由給到mutations,也就是會賦值給state

image-20221102180306127

這裡commit是為了給到state也就是為了左側的選單的一個顯示,而return是為了後面addRoutes這個方法,它是為了我們的url的一個路由顯示

5.4 許可權攔截出呼叫篩選許可權action

現在回到我們剛才所說的應該在路由守衛的那個位置來dispatch我們的篩選路由

image-20221102180929724

還沒完,我們還要發引數,在我們上面獲取使用者資訊的函式,專門return了一個返回值

image-20221102180846730

拿到返回值,傳引數

這裡為什麼後面這個也要接收返回值

image-20221102181229637

別忘了,我們的這個actions也定義了一個routes的return

image-20221102181446862

這裡有一個注意點,如果這裡呼叫addRoutes那就必須用next(to.path)不能直接用next()

這是vue-router的一個bug

image-20221102181930755

5.5 常量和非同步理由解除合併

image-20221102182204204

image-20221102182350602

現在url訪問自己有許可權的能訪問

image-20221102182429905

沒有許可權的為404

image-20221102182449870

image-20221102182459986

但是現在左側選單欄還沒有顯示出來

原因是因為這個模板左側選單遍歷的是路由表中的路由

image-20221102182633718

因為我們的addroutes並不是動態變化的,我們說的commit是給左側選單用的,這時候就發揮用場了

建立一個getters

image-20221102182801680

直接匯入

並且將我們之前的刪除

image-20221102182900283

六.登出重置路由許可權和404問題

首先是我們點選退出登入還會有問題,就是這個時候可以看到我們的state裡面的routes還為上一個使用者的許可權路由,並且我在url去輸入對應的路由還可以進入

image-20221102195613205

這是因為我們一直在addRoutes而沒有在登出去重置或者刪除他

在我們的vuerouter檔案裡面可以看到一個函式,這個是重置路由的函式

image-20221102195800761

那麼我們應該在登出的vuex裡面呼叫一下這個方法

image-20221102195854541

image-20221102195900458

然後第二部操作應該將state裡面的routes為初始狀態

我們應該呼叫這個mutations,然後傳進去的引數為空陣列

image-20221102200016530

問題來了,我們這裡是在一個vuex裡面要去commit兄弟級別的vuex裡面這個怎麼來做

image-20221102200058221

子模組呼叫子模組,兩種情況,一種是都沒加鎖的情況都沒有名稱空間的情況,那就可以直接呼叫,因為都預設是在全域性配置下的

第二種是都加了名稱空間的情況,我們的commit包括dispatch其實是有第三個引數的,第三個引數是一個物件,如果來一個root:true,表示呼叫根級的子模組

image-20221102200322992

解決第二個問題

當我們一重新整理就會出現404

看到我們的router配置檔案,有一個404的重定向路由,他有一個宣告說的必須放到最後,這裡我們由於把她放進了常量路由,又加了一些vuex裡面的路由再加上非同步路由導致他雖然這裡是在最後,但其實已經沒有在最後的位置了

image-20221102200602126

所以我們應該把這一段刪除,然後新增在我們addRoutes這裡

image-20221102200708173

七.功能許可權應用

7.1 功能許可權的受控思路

前面我們完成了頁面的訪問權,但是該頁面中某些功能使用者可能有也可能沒有,這就是功能受限

也是在我們userInfo資料裡面的points這個屬性裡面

image-20221102204141195

就是我們許可權管理下頁面許可權下

image-20221102204219035

這就表示有新增的按鈕許可權

image-20221102204342896

image-20221102204431118

就是讓有這個許可權的按鈕顯示,沒有就隱藏

7.2 使用mixin技術將檢查方法注入

定義規則

image-20221102205041482

全域性混入

image-20221102205119868

然後那個按鈕有許可權就呼叫這個方法,引數為這個按鈕的許可權標識

image-20221102205426591

image-20221102205540450

當然你直接是想連看都看不到

image-20221102205600013

相關文章