1. 程式人生 > >Vue 前端應用實現RBAC許可權控制的一種方式

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

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

  • 身份認證許可權控制
  • 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前端許可權控制

效果如下:

使用方法:

  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)
複製程式碼

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