vue許可權問題解決方案

qiugu發表於2019-05-07

前言

最近一直在忙著一個用vue來做的許可權管理的專案,其實在此之前,我也研究過vue的許可權如何實現,並且也為之寫過一篇部落格,但當真正應用在專案中的時候,還是發現了許多問題,所以此篇也會就著我在專案中遇到的一些問題,拿出來和大家分享一下,當然示例程式碼還是我的github倉庫中的ant-design-vue-ms

許可權問題解決思路

對於一個前後端分離的專案而言,許可權不再是僅僅靠後端來控制,後端只能控制介面的許可權,前臺的頁面顯示還是需要我們來控制,針對vue的專案,首先我想的是當許可權不多,並且都是單個許可權的情況下,我們完全沒有必要使用vue中提供的addRoutes的方法,可以使用動態元件來做,即我們根據後端返回的角色,來細度控制動態元件的顯示內容,所謂動態元件其實就是vue內建提供的component元件

<component :is="currentComponent"/>
複製程式碼

相信看到這裡,熟悉的同學應該已經想起來了,這樣的話,我們就不需要用到vuex,以及路由配置等等複雜的問題,單純靠後臺返回的角色名稱就能解決所有的問題了,看到這裡是不是覺得今天的內容就這些了,彆著急,下面還有“好看的”。

許可權設定中的問題

這樣雖然能解決一些簡單許可權的問題,但是針對稍微複雜一些的許可權應用,就顯得有些力不從心了,當角色過多,並且還包含了混合角色的許可權的話,則會衍生出很多問題,這裡也是列舉我遇到的一些問題,同學們可以細細推敲一下。

  • 如果是混合角色的話,動態元件的路由跳轉實際都是跳轉到一個頁面,但是混合角色肯定會一個頁面中跳到不同角色的頁面,這樣可能我們要多寫很多層的判斷,許可權混合越多,就越難以去判斷。
  • 動態元件擴充套件性比較差,如果我們再新增一個許可權呢,就要再多加一個動態元件的內容,並且出現混合許可權的話,那改動的地方就更多了

所以綜上所述,最終我還是選擇了傳統的addRoutes,那麼肯定會有同學問了,既然這個方案不行,那幹嘛還要用呢。問得好,其實動態元件就是一種嘗試,只有知道錯了,不滿足需求了,我們才能更知道為什麼會去使用傳統的addRoutes的許可權方案。

許可權問題解決方法

所以我們來看看addRoutes帶來的一些“好處”:

  • 一次配置,多處使用,我們配置好了動態路由以後,不論後期新增多少許可權,都能很好的顯示路由跳轉等等,並且也不需要改動程式碼,只需要新增新增角色的模組就可以了。
  • 遇到混合角色的問題,如果內容佈局類似的話,我們可以使用自定義指令來區分要顯示的模組,這樣的話如果一個賬號同時擁有很多角色的話,那麼包含這個角色的模組則會相應的顯示出來,就不會出現需要判斷究竟顯示哪個模組了,也不需要單獨為某個角色去設定一個頁面來顯示了。

相信做過許可權的同學對上面的內容還是有一些心得的,然後我們按照該有的步驟一步一步來,這些步驟在上面我的github中已經有了,大家可以對照一下。

  1. 全域性導航守衛的設定,此處設定全域性導航守衛,我覺得更多是為了資料持久化,大家都知道,vuex雖然非常好用,但是會有重新整理丟失資料的情況,因此針對這種情況,我們使用導航守衛,每次重新整理的時候,會重新請求後臺的介面來獲取角色資訊。
if (store.getters.roles.length === 0) {
        store
          .dispatch('GetInfo')
          .then(res => {
            const roles = res.data.resultData && res.data.resultData.roles
            store.dispatch('GenerateRoutes', { roles }).then(() => {
              // 根據roles許可權生成可訪問的路由表
              // 動態新增可訪問路由表
              router.addRoutes(store.getters.addRouters)
            })
          })
          .catch(() => {
            store.dispatch('Logout').then(() => {
              next({ path: '/user/login', query: { redirect: to.fullPath } })
            })
          })
      } else {
        next()
      }
複製程式碼

這裡程式碼做了簡化,主要給大家看下上面會有一個角色判斷長度,主要是當我們不重新整理的情況,頁面角色資訊不回丟失,因此我們也就沒有必要去請求後臺獲取角色資訊了,來節省請求數量。 2. 通過上面的程式碼可以看到,我們首先是請求的角色資訊,然後請求了生成路由的GenerateRoutes的方法,方法是寫在vuex中的action裡面的,這部分的內容因為網上有很多教程,其實主要歸納一下,就是對路由進行遞迴過濾,過濾出符合角色的路由,然後將靜態路由和過濾出來的動態路由連結起來

const permission = {
  state: {
    routers: constRouterMap,
    addRouters: []
  },
  mutations: {
    SET_ROUTERS: (state, routers) => {
      state.addRouters = routers
      state.routers = constRouterMap.concat(routers)
    }
  },
  actions: {
    GenerateRoutes({ commit }, data) {
        //略
    }
  }
}
複製程式碼
  1. 設定我們的路由檔案,這部分放到這裡來說,主要因為這裡還有個小坑,所以也是特地拿出來和大家分享一下
export const constRouterMap = [
    {
        path: '/',
        redirect: '/index',
        component: BasicLayout,
        children: [
            {
                path: '/index',
                name: 'index',
                // route level code-splitting
                // this generates a separate chunk (about.[hash].js) for this route
                // which is lazy-loaded when the route is visited.
                component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue'),
                meta: {
                    title: '儀表盤'
                }
            },
            {
                path: '/home',
                name: 'home',
                component: () => import(/* webpackChunkName: "home" */ '@/views/Home.vue'),
                meta: {
                    title: '表單頁'
                }
            },
            {
                path: '/pattern',
                name: 'pattern',
                component: () => import(/* webpackChunkName: "pattern" */ '@/views/DesignPattern.vue')
            },
            {
                path: '/map',
                name: 'map',
                component: () => import(/* webpackChunkName: "map" */ '@/views/DataMap.vue'),
                meta: {
                    title: '地圖元件'
                }
            },
        ]
    },
    {
        path: '/user',
        redirect: '/login',
        component: UserLayout,
        children: [
            {
                path: '/login',
                name: 'login',
                component: () => import(/* webpackChunkName: "login" */ '@/views/user/Login.vue')
            },
            {
                path: '/register',
                name: 'register',
                component: () => import(/* webpackChunkName: "login" */ '@/views/user/Register.vue')
            }
        ]
    },
    //需要注意這裡,404的路由一定要寫在靜態路由中
    {
        path: '/404',
        component: () => import(/* webpackChunkName: "not_found" */ '@/views/NotFound.vue')
    }
]

export const asyncRouterMap = [
    {
        path: '/',
        redirect: '/index',
        component: BasicLayout,
        children: [
            {
                path: '/controls',
                name: 'controls',
                component: () => import(/* webpackChunkName: "controls" */ '@/views/Controls.vue'),
                meta: {
                    title: '許可權設定',
                    permission: ['admin']
                }
            }
        ]
    },
    //捕獲未定義的路由配置
    {
        path: '*',
        redirect: '/404',
        hidden: true
    }
]

複製程式碼

上面關於404頁面的定義順序非常重要,如果在靜態路由中定義了捕獲的路由path:"*",而在動態路由中定義了404路由的話,則當導航鉤子中判斷比較複雜的話,會出現一些意想不到的錯誤,我就是當時寫反了順序,並且還在導航鉤子中做了一些複雜的麵包屑的判斷,一旦重新整理頁面的話,則會出現以下錯誤

vue許可權問題解決方案
這種錯誤的產生,可能是因為重新整理時,導航鉤子發現動態新增進來的路由找不到一直進行獲取動態路由的方法,導致最後呼叫棧溢位所導致,因此大家在使用的時候一定要非常小心。 4. 當我們生成路由後,退出應用的切換新的角色賬號進行登入時,一定要記得的兩件事,第一就是清空vuex裡面的角色資訊,在不重新整理的情況下,這些資訊是不會丟失的,當不同角色的賬號登入時,原來的角色依然存在,那麼肯定會出現問題,其次則是在跳轉會登入頁的時候,需要設定重新整理頁面的程式碼

window.location.reload();
this.$router.push({name: 'login'});
複製程式碼

先重新整理以後再跳轉到登入頁,這個則是因為addRoutes生成的路由在不重新整理的情況下會一直存在,即使下個不同角色的賬號登入時,依然會拿之前存在的路由資訊去進行過濾,這樣過濾的結果必然是當前角色的路由一個都不存在,因此生成的路由資訊還是上個角色的路由,所以在完成了之前這些步驟時,一定不要忘記了做這兩步,這樣也才是一個完整的許可權解決方案

尾聲

以上也是我在專案中一些收貨吧,拿出來和大家分享,也是希望大家少走一些彎路,留心我們開發中遇到的每個看似很小的問題,其實往往是我們最後解決問題的關鍵,不論是從動態元件還是動態路由,問題的出現也是我們不斷去完善自己的過程。

相關文章