Vue 前端應用實現RBAC許可權控制的一種方式

趙津發表於2018-12-19

上午釋出前,感覺已經做完了,就接著擼jiiiiiin許可權系統 ,可是我發現漏了一個東西,就是一更的時候,針對v-access指令預設模式,通過校驗對應資源所需介面列表來檢查登入使用者是否具有訪問許可權,但是那個時候的對比是介面的url,資料結構如下:

authorizeInterfaces:

["admin/dels/*", "admin/search/*/*/*", "admin/*/*/*", "role/list/*", "admin/*"]
複製程式碼

但是我接著寫的時候,發現不滿足呀,我後臺的介面是RESTful型別的,所以必須校驗url+method,那現在的資料結構就變成:

authorizeInterfaces:

[{url: "admin/dels/*", method: "DELETE"}, ....]
複製程式碼

故由修改了一下,具體可以參考下面的commit:

修改rbac前端校驗模組,使其支援RESTful型別介面校驗

Vue 前端應用實現RBAC許可權控制的一種方式

對於使用就兩步: 1.增加了一個isRESTfulInterfaces配置:

/**
     * [*] 宣告`authorizeInterfaces`集合儲存的是RESTful型別的介面還是常規介面
     * 1. 如果是(true),則`authorizeInterfaces`集合需要儲存的結構就是:
     * [{url: 'admin/dels/*', method: 'DELETE'}]
     * 即進行介面匹配的時候會校驗型別
     * 2. 如果不是(false),則`authorizeInterfaces`集合需要儲存的結構就是,即不區分介面型別:
     * ['admin/dels/*']
     */
    isRESTfulInterfaces = true
複製程式碼

2.在登入成功設定$vp.rabcUpdateAuthorizeInterfaces已授權介面列表的時候,大家需要將資料結構構造好。

3.針對於RESTful型別介面:v-access="[{url: 'admin/search/*', method: 'POST'}]"

現在針對v-access指令的值的使用,可以有以下幾種情況:

  • 如果isRESTfulInterfaces設定為true注意這是預設設定,則使用下面的格式:
v-access="[{url: 'admin/search/*', method: 'POST'}]"
// 或者如果只有一個介面宣告
v-access="{url: 'admin/search/*/*/*', method: 'POST'}"
複製程式碼
  • 如果isRESTfulInterfaces設定為false,則使用下面的格式:
v-access="['admin', 'admin/*']"
複製程式碼
  • 如果希望使用別名標識一個資源:
v-access:alias="['LOGIN', 'WELCOME']"
v-access:alias="'LOGIN'"
複製程式碼
  • 另外以上幾種都是如何宣告元件所需許可權,而如果登入使用者沒有這個許可權,則元件將會被隱藏,但是也可以使用下面的配置讓元件變為半透明且不可用點選:
v-access:alias.disable="['LOGIN', 'WELCOME']"
v-access.disable="['admin', 'admin/*']"
複製程式碼

就是加一個disable而已;

:)

謝謝支援!

更新分割


許可權控制不管前後端都可以簡單分為:

  • 身份認證許可權控制
  • RBAC許可權控制
  • ...

而前端我和團隊,檢索了很多地方都沒有很成熟或者說可行的關於 RBAC基於角色的訪問控制相關的前端許可權控制方案,可能是我們檢索的方法不對,亦或是大家都忙於其他,沒有時間把自己的方法整理公佈出來,故我們在原定計劃中,自己實踐了一把,下面和大家分享一下,有不對或者錯誤的地方,望指正。

如果大家點進來,應該都知道何為RBAC,為什麼需要使用了,故這裡對此不做過多解釋,RBAC基於角色的訪問控制,一般只會在管理端應用使用,故這個模組不作為預設模組。

下面介紹一下vue-viewplus 一個簡化Vue應用開發的工具庫中的rabc.js 自定義RBAC許可權控制模組。

該模組,意在為前端應用提供rbac許可權控制幫助。

其和login-state-check.js 身份認證許可權控制模組不同之處在於,該模組提供了一下兩種許可權控制手段:

  • 實現前端頁面可訪問性控制,即通過路由攔截,判斷使用者待訪問頁面是否已經授權
  • 實現可見頁面的區域性UI元件的可使用性或可見性控制,即基於自定義v-access指令,對比宣告的介面或資源別是否已經授權

而login-state-check.js 身份認證許可權控制模組,則提供的是對非公共頁面的身份認證校驗檢查,其中維護了使用者的身份認證即登入狀態,這種許可權控制,更適合大多數應用,即給使用者使用的客戶端應用。

而當前模組也依賴了登入狀態,故可以一起復用;

實際案例:

名稱 渠道 簡介
jiiiiiin許可權系統 PC端 一個前後端分離的內管基礎專案,並基於當前外掛完成了RBAC前端許可權控制

效果如下:

Vue 前端應用實現RBAC許可權控制的一種方式
Vue 前端應用實現RBAC許可權控制的一種方式
Vue 前端應用實現RBAC許可權控制的一種方式
Vue 前端應用實現RBAC許可權控制的一種方式

使用方法:

  1. 基於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');
            }
        })
    }
})
複製程式碼
  1. 在登入成功之後,需要設定外掛的登入狀態,和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)
複製程式碼

針對需要設定的許可權集合,其都是扁平化的一維陣列,格式類似:

authorizedPathspublicPaths:

["/mngauth/admin", "/index", "/mngauth"]
複製程式碼

authorizeInterfaces:

["admin/dels/*", "admin/search/*/*/*", "admin/*/*/*", "role/list/*", "admin/*"]
複製程式碼

authorizeResourceAlias:

["MNG_USERMNG", "MNG_ROLEMNG"]
複製程式碼

注意以上陣列的值除了可以配置為字串還可以配置為正規表示式:

[/^((\/Interbus)(?!\/SubMenu)\/.+)$/]
複製程式碼
  1. 實現可見頁面的區域性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)
複製程式碼

如果大家覺得有用,請大家多多支援,更多的模組請點選檢視

相關文章