vue router 路由鑑權(非動態路由)

概述

  • 角色:超級管理員、主題管理員、數據服務管理員
  • 權限:
    超級管理員:全部頁面
    主題管理員:基礎公共頁面+主題設置頁
    數據服務管理員:基礎公共頁面+數據服務設置頁+數據服務審批頁
  • 需求:角色菜單來自後端,當用戶未經過頁面菜單,直接從地址欄訪問非權限範圍內的url時,攔截用戶訪問並重定向到首頁。

實際系統中還有幾種管理員,此處略去,以精簡描述。 javascript

本來想用動態路由的思路去作,按權限加載對應路由表,可是因爲權限能夠交叉(好比一我的能夠同時是主題管理員和數據服務管理員),致使權限路由表仍是得去作判斷組合。因而放棄了這個思路,索性就在beforeEach裏直接判斷了。vue

實現

路由概覽

// index.js
import Vue from 'vue'
import Router from 'vue-router'

import LabelMarket from './modules/label-market'
import PersonalCenter from './modules/personal-center'
import SystemSetting from './modules/system-setting'

import API from '@/utils/api'

Vue.use(Router)

const routes = [
  {
    path: '/label',
    component: () => import(/* webpackChunkName: "index" */ '@/views/index.vue'),
    redirect: { name: 'LabelMarket' },
    children: [
      { // 基礎公共頁面
        path: 'label-market',
        name: 'LabelMarket',
        component: () => import(/* webpackChunkName: "label-market" */ '@/components/page-layout/OneColLayout.vue'),
        redirect: { name: 'LabelMarketIndex' },
        children: LabelMarket
      },
      { // 我的中心
        path: 'personal-center',
        name: 'PersonalCenter',
        redirect: '/label/personal-center/my-apply',
        component: () => import(/* webpackChunkName: "personal-center" */ '@/components/page-layout/TwoColLayout.vue'),
        children: PersonalCenter
      },
      { // 系統設置
        path: 'system-setting',
        name: 'SystemSetting',
        redirect: '/label/system-setting/theme',
        component: () => import(/* webpackChunkName: "system-setting" */ '@/components/page-layout/TwoColLayout.vue'),
        children: SystemSetting
      }]
  },
  {
    path: '*',
    redirect: '/label'
  }
]

const router = new Router({ mode: 'history', routes })
// personal-center.js
export default [
    ...
  { // 個人審批
    path: 'my-approve',
    name: 'PersonalCenterMyApprove',
    component: () => import(/* webpackChunkName: "personal-center" */ '@/views/personal-center/index.vue'),
    children: [
      { // 數據服務審批
        path: 'api',
        name: 'PersonalCenterMyApproveApi',
        meta: {
          requireAuth: true,
          authRole: 'dataServiceAdmin'
        },
        component: () => import(/* webpackChunkName: "personal-center" */ '@/views/personal-center/api-approve/index.vue')
      },
      ...
    ]
  }
]
export default [
    ...
  { // 數據服務設置
    path: 'api',
    name: 'SystemSettingApi',
    meta: {
      requireAuth: true,
      authRole: 'dataServiceAdmin'
    },
    component: () => import(/* webpackChunkName: "system-setting" */ '@/views/system-setting/api/index.vue')
  },
  { // 主題設置
    path: 'theme',
    name: 'SystemSettingTheme',
    meta: {
      requireAuth: true,
      authRole: 'topicAdmin'
    },
    component: () => import(/* webpackChunkName: "system-setting" */ '@/views/system-setting/theme/index.vue')
  },
    ...
]

鑑權判斷

用戶登錄信息請求後端接口,返回菜單、權限、版權信息等公共信息,存入vuex。此處用到權限字段以下:java

_userInfo: {
    admin:false, // 是否超級管理員
    dataServiceAdmin:true, // 是否數據服務管理員
    topicAdmin:false // 是否主題管理員
}

權限判斷邏輯以下:webpack

  1. 判斷當前路由是否須要鑑權(router中meta字段下requireAuth是否爲true),讓公共頁面直接放行;
  2. 判斷角色是超級管理員,直接放行;
  3. (本系統特殊邏輯)判斷跳轉路徑是主題設置但角色不爲主題管理員,繼續判斷角色是否爲數據服務管理員,跳轉數據服務設置頁or重定向(‘系統設置’菜單'/label/system-setting'默認重定向到'/label/system-setting/theme',其餘菜單默認重定向的都是基礎公共頁面,故須要對這裏的重定向鑑權。系統設置的權限不是主題管理員就必定是數據服務管理員,因此能這樣作);
  4. 判斷路由需求權限是否符合,若不符合直接重定向。
// index.js
router.beforeEach(async (to, from, next) => {
  try {
    // get user login info
    const _userInfo = await API.get('/common/query/menu', {}, false)
    router.app.$store.dispatch('setLoginUser', _userInfo)

    if (_userInfo && Object.keys(_userInfo).length > 0 &&
      to.matched.some(record => record.meta.requireAuth)) {
      if (_userInfo.admin) { // super admin can pass
        next()
      } else if (to.fullPath === '/label/system-setting/theme' &&
        !_userInfo.topicAdmin) {
        if (_userInfo.dataServiceAdmin) {
          next({ path: '/label/system-setting/api' })
        } else {
          next({ path: '/label' })
        }
      } else if (!(_userInfo[to.meta.authRole])) {
        next({ path: '/label' })
      }
    }
  } catch (e) {
    router.app.$message.error('獲取用戶登錄信息失敗!')
  }
  next()
})
相關文章
相關標籤/搜索