Vue 前端應用實現RBAC許可權控制的一種方式
許可權控制不管前後端都可以簡單分為:
- 身份認證許可權控制
- RBAC許可權控制
- ...
而前端我和團隊,檢索了很多地方都沒有很成熟或者說可行的關於 RBAC基於角色的訪問控制相關的前端許可權控制方案,可能是我們檢索的方法不對,亦或是大家都忙於其他,沒有時間把自己的方法整理公佈出來,故我們在原定計劃中,自己實踐了一把,下面和大家分享一下,有不對或者錯誤的地方,望指正。
如果大家點進來,應該都知道何為RBAC,為什麼需要使用了,故這裡對此不做過多解釋,RBAC基於角色的訪問控制,一般只會在管理端應用使用,故這個模組不作為預設模組。
下面介紹一下vue-viewplus 一個簡化Vue應用開發的工具庫
該模組,意在為前端應用提供rbac許可權控制幫助。
其和login-state-check.js 身份認證許可權控制模組不同之處在於,該模組提供了一下兩種許可權控制手段:
- 實現前端頁面可訪問性控制,即通過路由攔截,判斷使用者待訪問頁面是否已經授權
- 實現可見頁面的區域性UI元件的可使用性或可見性控制,即基於自定義
v-access
指令,對比宣告的介面或資源別是否已經授權
而login-state-check.js 身份認證許可權控制模組,則提供的是對非公共頁面的身份認證校驗檢查,其中維護了使用者的身份認證即登入狀態,這種許可權控制,更適合大多數應用,即給使用者使用
而當前模組也依賴了登入狀態,故可以一起復用;
實際案例:
名稱 | 渠道 | 簡介 |
---|---|---|
jiiiiiin許可權系統 | PC端 | 一個前後端分離的內管基礎專案,並基於當前外掛完成了RBAC前端許可權控制 |
效果如下:
使用方法:
- 其基於vue-viewplus,實現了一個自定義模組 ,非標準模組,需要手動配置:
main.js入口檔案:
import router from './router'
import ViewPlus from 'vue-viewplus'
import rbacModule from '@/plugin/vue-viewplus/rbac.js'
import viewPlusOptions from '@/plugin/vue-viewplus'
Vue.use(ViewPlus, viewPlusOptions)
ViewPlus.mixin(Vue, rbacModule, {
debug: true,
errorHandler(err) {
console.error(err)
},
moduleName: '自定義RBAC',
router,
publicPaths: ['/login'],
onLoginStateCheckFail(to, from, next) {
this.dialog(`您無權訪問【${to.path}】頁面`)
.then(() => {
// 防止使用者被踢出之後,被許可權攔截導致訪問不了任何頁面,故這裡進行登入狀態監測
if (this.isLogin()) {
next(false);
} else {
next('/login');
}
})
}
})
複製程式碼
- 在登入成功之後,需要設定外掛的登入狀態,和rabc模組相應許可權集合,即後端返回的當前登入使用者擁有的:
-
[*] 登入使用者擁有訪問許可權的路由path路徑集合
完成該配置,則頁面可訪問性控制就可以正常工作
-
[*] 登入使用者擁有訪問許可權的後臺介面集合
-
[可選] 登入使用者擁有訪問許可權的資源別名集合
完成以上配置,則自定義v-access指令就可以支援對應模式的配置
-
[可選] 是否是超級使用者
有些系統存在一個超級使用者角色,其可以訪問任何資源、頁面,故如果設定,針對這個登入使用者將不會做任何許可權校驗,以便節省前端資源
// 開始請求登入介面
AccountLogin(vm.$vp, {
username,
password,
imageCode
})
.then(async res => {
// 修改使用者登入狀態
vm.$vp.modifyLoginState(true)
const menus = _delEmptyChildren(res.principal.admin.menus);
const authorizeResources = _parseAuthorizePaths(res.principal.admin.authorizeResources);
vm.$vp.rabcUpdateAuthorizedPaths(authorizeResources)
const authorizeInterfaces = _parseAuthorizeInterfaces(res.principal.admin.authorizeInterfaces);
vm.$vp.rabcUpdateAuthorizeInterfaces(authorizeInterfaces)
const isSuperAdminStatus = _parseUserRoleIsSuperAdminStatus(res.principal.admin.roles);
vm.$vp.rabcUpdateSuperAdminStatus(isSuperAdminStatus)
複製程式碼
針對需要設定的許可權集合,其都是扁平化的一維陣列,格式類似:
authorizedPaths
和publicPaths
:
["/mngauth/admin", "/index", "/mngauth"]
複製程式碼
authorizeInterfaces
:
["admin/dels/*", "admin/search/*/*/*", "admin/*/*/*", "role/list/*", "admin/*"]
複製程式碼
authorizeResourceAlias
:
["MNG_USERMNG", "MNG_ROLEMNG"]
複製程式碼
注意以上陣列的值除了可以配置為字串還可以配置為正則表示式:
[/^((\/Interbus)(?!\/SubMenu)\/.+)$/]
複製程式碼
- 實現可見頁面的區域性UI元件的可使用性或可見性配置示例:
<el-form v-access="['admin/search/*/*/*']" slot="search-inner-box" :inline="true" :model="searchForm" :rules="searchRules" ref="ruleSearchForm" class="demo-form-inline">
...
<el-form-item class="search-inner-btn-box">
<el-button size="small" type="primary" icon="el-icon-search" @click="onSearch">查詢</el-button>
<el-button size="small" icon="el-icon-refresh" @click="onCancelSubmit">重置</el-button>
</el-form-item>
</el-form>
複製程式碼
完成以上配置即可讓正常使用當前模組提供的許可權控制服務,當然如$vp.modifyLoginState|$vp#isLogin
涉及到login-state-check.js 身份認證許可權控制模組
計劃
針對authorizeInterfaces
,後期將會用於在傳送ajax請求之前,對待請求的介面和當前集合進行匹配,如果匹配失敗說明使用者就沒有請求許可權,則直接不傳送後臺請求,減少後端不必要的資源浪費,在完成這個許可權匹配,前端基礎的許可權規則就完整了。
其實實現下來沒有想象的那麼複雜,可以點選檢視原始碼
相較於理解這一塊,我覺得理解RBAC原則和表結構,才能更好的理解為什麼要這麼控制,更多的關於後端關於這一塊的實踐,可以參考jiiiiiin許可權系統這個內管專案針對表結構的設計,其後臺使用的是spring security來完成後端的RBAC許可權控制,並針對當前前端許可權需要和vue router path進行了細微變化,相較於傳統的RBAC 金典5張表的設計。
也請大家多多支援 :)
下面是改模組的api描述:
配置
debug|errorHandler|router|installed
配置,可以檢視全域性通用配置
publicPaths
/**
* [*] 系統公共路由path路徑集合,即可以讓任何人訪問的頁面路徑
* {Array<Object>}
* <p>
* 比如登入頁面的path,因為登入之前我們是無法判斷使用者是否可以訪問某個頁面的,故需要這個配置,當然如果需要這個配置也可以在初始化外掛之前從伺服器端獲取,這樣前後端動態性就更高,但是一般沒有這種需求:)
* <p>
* 陣列中的item,可以是一個**正則表示式字面量**,如`[/^((\/Interbus)(?!\/SubMenu)\/.+)$/]`,也可以是一個字串
* <p>
* 匹配規則:如果在`LoginStateCheck#publicPaths`**系統公共路由path路徑集合**中,那麼就直接跳過許可權校驗
*/
publicPaths = []
複製程式碼
authorizedPaths
/**
* [*] 登入使用者擁有訪問許可權的路由path路徑集合
* {Array<Object>}
* <p>
* 陣列中的item,可以是一個**正則表示式字面量**,如`[/^((\/Interbus)(?!\/SubMenu)\/.+)$/]`,也可以是一個字串
* <p>
* 匹配規則:如果在`LoginStateCheck#authorizedPaths`**需要身份認證規則集**中,那麼就需要檢視使用者是否登入,如果沒有登入就拒絕訪問
*/
authorizedPaths = []
複製程式碼
authorizeInterfaces
/**
* [*] 登入使用者擁有訪問許可權的後臺介面集合
* {Array<Object>}
* <p>
* 1.在`v-access`指令配置為url(預設)校驗格式時,將會使用該集合和指令宣告的待審查授權介面列表進行匹配,如果匹配成功,則指令校驗通過,否則校驗不通過,會將對應dom元素進行處理
* 2.TODO 將會用於在傳送ajax請求之前,對待請求的介面和當前集合進行匹配,如果匹配失敗說明使用者就沒有請求許可權,則直接不傳送後臺請求,減少後端不必要的資源浪費
* <p>
* 陣列中的item,可以是一個**正則表示式字面量**,如`[/^((\/Interbus)(?!\/SubMenu)\/.+)$/]`,也可以是一個字串
* <p>
* 匹配規則:將會用於在傳送ajax請求之前,對待請求的介面和當前集合進行匹配,如果匹配失敗說明使用者就沒有請求許可權,則直接不傳送後臺請求,減少後端不必要的資源浪費
*/
authorizeInterfaces = []
複製程式碼
authorizeResourceAlias
/**
* [可選] 登入使用者擁有訪問許可權的資源別名集合
* {Array<Object>}
* <p>
* 陣列中的item,可以是一個**正則表示式字面量**,如`[/^((\/Interbus)(?!\/SubMenu)\/.+)$/]`,也可以是一個字串
* <p>
* 匹配規則:因為如果都用`LoginStateCheck#authorizeInterfaces`介面進行匹配,可能有一種情況,訪問一個資源,其需要n個介面,那麼我們在配置配置許可權指令:v-access="[n, n....]"的時候就需要宣告所有需要的介面,就會需要對比多次,
* 當我們系統的介面集合很大的時候,勢必會成為一個瓶頸,故我們可以為資源宣告一個別名,這個別名則可以代表這n個介面,這樣的話就從n+減少到n次匹配;
*/
authorizeResourceAlias = []
複製程式碼
onLoginStateCheckFail
/**
* [*] `$vp::onLoginStateCheckFail(to, from, next)`
* <p>
* 許可權檢查失敗時被回撥
*/
onLoginStateCheckFail = null
複製程式碼
API介面
modifyLoginState
/**
* 代理`$vp#login-state-check`模組的同名方法,以實現在登出、會話超時踢出的時候清理本模組維護的登入之後設定的狀態
* @param status
*/
modifyLoginState(status = false)
複製程式碼
rabcUpdateSuperAdminStatus
/**
* 【可選】有些系統存在一個超級使用者角色,其可以訪問任何資源、頁面,故如果設定,針對這個登入使用者將不會做任何許可權校驗,以便節省前端資源
* @param status
*/
rabcUpdateSuperAdminStatus(status)
複製程式碼
rabcAddAuthorizedPaths
/**
* 新增授權路徑集合
* 如:登入完成之後,將使用者被授權可以訪問的頁面`paths`新增到`LoginStateCheck#authorizedPaths`中
* @param paths
*/
rabcAddAuthorizedPaths(paths)
複製程式碼
rabcUpdateAuthorizedPaths
/**
* 更新授權路徑集合
* @param paths
*/
rabcUpdateAuthorizedPaths(paths)
複製程式碼
rabcUpdateAuthorizeResourceAlias
/**
* 更新資源別名集合
* @param alias
*/
rabcUpdateAuthorizeResourceAlias(alias)
複製程式碼
rabcAddAuthorizeResourceAlias
/**
* 新增資源別名集合
* @param alias
*/
rabcAddAuthorizeResourceAlias(alias)
複製程式碼
rabcUpdatePublicPaths
/**
* 更新公共路徑集合
* @param paths
*/
rabcUpdatePublicPaths(paths)
複製程式碼
rabcAddPublicPaths
/**
* 新增公共路徑集合
* @param paths
*/
rabcAddPublicPaths(paths)
複製程式碼
如果大家覺得有用,請大家多多支援,更多的模組請點選檢視