Vue2.0 + ElementUI 手寫許可權管理系統後臺模板(二)——許可權管理

夏洛克丶旭發表於2018-12-16

許可權驗證

頁面級別許可權

路由:

預設掛載不需要許可權的路由,例如:登入、主頁。需要許可權的頁面通過 router.addRoutes(點選檢視官方文件) 動態新增更多的路由規則,404攔截頁面需要放在路由表的最後,否則 /404 後面的路由會被404攔截,通過路由元資訊meta(點選檢視官方文件)記錄路由需要的許可權。為了選單列表可以被翻譯,路由表的 name 屬性值通過 i18n 的英文對照表來獲取,也可以直接寫英文名稱,如 name: routeNmae.builtInIcon 可以直接寫成 name: "builtInIcon",憑個人喜好

// src/router/index.js
import en from '../i18n/lang/en' // 英文對照表
import Vue from 'vue'
import Router from 'vue-router'
import CommerViews from '@/views/commerViews'
import Login from '@/views/login/index'
import Layout from '@/views/layout/layout'
import HomeMain from '@/views/index/mainIndex'

// 不是必須載入的元件使用懶載入
const Icon = () => import('@/views/icon/index')
const Upload = () => import('@/views/upload/upload')
const Markdown = () => import('@/views/markdown/markdownView')
const NotFound = () => import('@/page404')

Vue.use(Router)
let routeNmae = en.routeNmae    // 從英文翻譯對照表獲取路由的英文名字,當做路由'name'屬性的值 

// 不需要許可權的路由
let defaultRouter = [
  { path: '/',
    redirect: '/index',
    hidden: true,
    children: []
  },
  {
    path: '/login',
    component: Login,
    name: '',
    hidden: true,
    children: []
  },
  {
    path: '/index',
    iconCls: 'fa fa-dashboard', // 選單圖示,直接填寫字型圖示的 class
    name: routeNmae.home,
    component: Layout,
    alone: true,
    children: [
      {
        path: '/index',
        iconCls: 'fa fa-dashboard',
        name: '主頁',
        component: HomeMain,
        children: []
      }
    ]
  },
  {
    path: '/404',
    component: NotFound,
    name: '404',
    hidden: true,
    children: []
  },
]

// 需要 addRouters 動態載入的路由 
let addRouter = [
  {
    path: '/',
    iconCls: 'fa fa-server',
    name: routeNmae.multiDirectory,
    component: Layout,
    children: [
      {
        path: '/erji1',
        iconCls: 'fa fa-server',
        name: routeNmae['menu2-1'],
        component: Erji,
        children: []
      },
      {
        path: '/erji3',
        iconCls: 'fa fa-server',
        name: routeNmae['menu2-3'],
        component: CommerViews, // 無限極選單的容器 超過三級選單父級容器需要使用 CommerViews
        children: [
          {
            path: '/sanji2',
            iconCls: 'fa fa-server',
            name: routeNmae['menu3-2'],
            component: Sanji2,
            children: []
          },
          {
            path: '/sanji3',
            iconCls: 'fa fa-server',
            name: routeNmae['menu3-3'],
            component: CommerViews,
            children: [
              {
                path: '/siji',
                iconCls: 'fa fa-server',
                name: routeNmae['menu4-1'],
                component: Siji,
                children: []
              },
              {
                path: '/siji1',
                iconCls: 'fa fa-server',
                name: routeNmae['menu4-2'],
                component: CommerViews,
                children: [
                  {
                    path: '/wuji',
                    iconCls: 'fa fa-server',
                    name: routeNmae['menu5-1'],
                    component: Wuji,
                    children: []
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  },
  
  {
    path: '/',
    iconCls: 'el-icon-edit', // 圖示樣式class
    name: routeNmae.editor,
    component: Layout,
    meta: {role: ['superAdmin', 'admin']}, // 需要許可權 'superAdmin', 'admin'。meta屬性可以放在父級,驗證父級和所有子選單,也可以放在子級單獨驗證某一個子選單
    children: [
      {
        path: '/markdown',
        iconCls: 'fa fa-file-code-o', // 圖示樣式class
        name: routeNmae.markdown,
        component: Markdown,
        children: []
      }
    ]
  },
  { path: '*',  // 萬用字元攔截放在最後,不存在的路由全都指向404頁面
    redirect: '/404',
    hidden: true,
    children: []
  },

]
export default new Router({
  routes: defaultRouter
})
export {defaultRouter, addRouter}

複製程式碼

然後通過 token 獲取當前登入使用者的個人資訊,在router被掛載到Vue之前和需要許可權的路由表做對比,篩選出當前角色有許可權訪問的動態路由表,

// main.js
//  獲取角色資訊,根據使用者許可權動態載入路由
router.beforeEach((to, from, next) => {
  if (store.getters.token) { // 檢視 token 是否存在
    store.dispatch('setToken', store.getters.token) // 每次操作都重新寫入 token,更新有效會話時長
    if (to.path === '/login') {
      next({path: '/'})
    } else {
      if (!store.getters.info.role) { // 檢視是否有當前使用者角色,如果沒有則獲取角色資訊
        !async function getAddRouters () {
          await store.dispatch('getInfo', store.getters.token) // 通過token獲取角色資訊
          await store.dispatch('newRoutes', store.getters.info.role) // 通過許可權篩選新路由表
          await router.addRoutes(store.getters.addRouters) // 動態載入新路由表
          next({path: '/index'})
        }()
      } else {
        let is404 = to.matched.some(record => { // 404頁面攔截
          if(record.meta.role){
          // 沒有許可權的頁面,跳轉的404頁面
            return record.meta.role.indexOf(store.getters.info.role) === -1
          }
        })
        if(is404){
          next({path: '/404'})
          return false
        }
        next()
      }
    }
  } else {
    if (to.path === '/login') {
      next()
    }
    next({path: '/login'})

  }
})
複製程式碼

actions: getInfo

// src/vuex/modules/role.js
state: {
    info: '' // 每次重新整理都要通過token請求個人資訊來篩選動態路由
  },
  mutations: {
    getInfo (state, token) {
      // 省略 axios 請求程式碼 通過 token 向後臺請求使用者許可權等資訊,這裡用假資料賦值
      state.info = {
        role: 'superAdmin',
        permissions: '超級管理員'
      }
      // 將 info 儲存在 sessionStorage裡, 按鈕指令許可權將會用到
      sessionStorage.setItem('info', JSON.stringify(store.getters.info))
    },
    setRole (state, options) {  // 切換角色,測試許可權管理
      state.info = {
        role: options.role,
        permissions: options.permissions
      }
      sessionStorage.setItem('info', JSON.stringify(store.getters.info));
      // 許可權切換後要根據新許可權重新獲取新路由,再走一遍流程
      store.dispatch('newRoutes', options.role)
      router.addRoutes(store.getters.addRouters)

    }
  },
  actions: {
    getInfo ({commit}, token) {
      commit('getInfo', token)
    },
    setRole ({commit}, options){// 切換角色,測試許可權管理,不需要可以刪除
      commit('setRole', options)
    }
  }
複製程式碼

actions: newRoutes

// src/vuex/modules/routerData.js
import {defaultRouter, addRouter} from '@/router/index'
const routerData = {
state: {
    routers: [],
    addRouters: []
  },
  mutations: {
    setRouters: (state, routers) => {
      state.addRouters = routers  // 儲存動態路由用來addRouter
      state.routers = defaultRouter.concat(routers) // 所有有許可權的路由表,用來生成選單列表
    }
  },
  actions: {
    newRoutes ({commit}, role) {
      //  通過遞迴路由表,刪除掉沒有許可權的路由
      function eachSelect (routers, userRole) {
        for (let j = 0; j < routers.length; j++) {
          if (routers[j].meta && routers[j].meta.role.length && routers[j].meta.role.indexOf(userRole) === -1) {
          // 如果沒有許可權就刪除該路由,如果是父級路由沒許可權,所有子選單就更沒許可權了,所以一併刪除
            routers.splice(j, 1)
            j = j !== 0 ? j - 1 : j // 刪除掉沒有許可權的路由後,下標應該停止 +1,保持不變,如果下標是 0的話刪除之後依然等於0
          }
          if (routers[j].children && routers[j].children.length) {
           //  如果包含子元素就遞迴執行
            eachSelect(routers[j].children, userRole)
          }
        }
      }
      // 拷貝這個陣列是因為做許可權測試的時候可以從低階切回到高階角色,僅限演示,正式開發時省略這步直接使用 addRouter
      // 僅限演示
      let newArr = [...addRouter]
      eachSelect(newArr, role)
      commit('setRouters', newArr)

      // 正式開發
      // eachSelect(addRouter, role)
      // commit('setRouters', addRouter)
    }
  }
}
export default routerData
複製程式碼

按鈕級別許可權驗證

通過自定義指令獲取當前按鈕所需的有哪些許可權,然後和當前使用者的許可權對比,如果沒有許可權則刪除按鈕

// btnPermission.js
import Vue from 'vue'

Vue.directive('roleBtn',{
  bind:function (el,binding) {
    let roleArr = binding.value; // 獲取按鈕所需許可權
    let userRole =  JSON.parse(sessionStorage.getItem('info')).role // 獲取當前使用者許可權
    if (roleArr && roleArr.indexOf(userRole) !== -1) {
      return false
    } else {
      el.parentNode.removeChild(el);
    }
  }
})
export default Vue
複製程式碼

使用自定義指令許可權

<el-button type="primary" plain size="medium">檢視</el-button>
<el-button type="primary" plain size="medium" v-role-btn="['admin']">新增</el-button>
<el-button type="danger" plain size="medium" v-role-btn="['superAdmin']">刪除</el-button>
<el-button type="primary" plain size="medium" v-role-btn="['superAdmin','admin']">修改</el-button>
複製程式碼

系列文章

Vue2.0 + ElementUI 手寫許可權管理系統後臺模板(一)——簡述

Vue2.0 + ElementUI 手寫許可權管理系統後臺模板(二)——許可權管理

Vue2.0 + ElementUI 手寫許可權管理系統後臺模板(三)——頁面搭建

Vue2.0 + ElementUI 手寫許可權管理系統後臺模板(四)——元件結尾

相關文章