国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

如何優雅地在vue中添加權限控制示例詳解

2019-11-19 12:01:39
字體:
來源:轉載
供稿:網友

前言

在一個項目中,一些功能會涉及到重要的數據管理,為了確保數據的安全,我們會在項目中加入權限來限制每個用戶的操作。作為前端,我們要做的是配合后端給到的權限數據,做頁面上的各種各樣的限制。

需求

因為這是一個工作上的業務需求,所以對于我來說主要有兩個地方需要進行權限控制。

第一個是側邊菜單欄,需要控制顯示與隱藏。

第二個就是頁面內的各個按鈕,彈窗等。

流程

1、如何獲取用戶權限?

后端(當前用戶擁有的權限列表)-> 前端(通過后端的接口獲取到,下文中我們把當前用戶的權限列表叫做 permissionList)

2、前端如何做限制?

通過產品的需求,在項目中進行權限點的配置,然后通過 permissionList 尋找是否有配置的權限點,有就顯示,沒有就不顯示。

3、然后呢?

沒了。

當我剛開始接到這個需求的時候就是這么想的,這有什么難的,不就獲取 permissionList 然后判斷就可以了嘛。后來我才發現真正的需求遠比我想象的復雜。

真正的問題

上面的需求有提到我們主要解決兩個問題,側邊菜單欄的顯示 & 頁面內操作。

假設我們有這樣一個路由的設置(以下只是一個例子):

import VueRouter from 'vue-router'/* 注意:以下配置僅為部分配置,并且省去了 component 的配置 */export const routes = [ { path: '/', name: 'Admin', label: '首頁' }, { path: '/user', name: 'User', label: '用戶', redirect: { name: 'UserList' }, children: [ { path: 'list', name: 'UserList', label: '用戶列表' }, { path: 'group', name: 'UserGroup', label: '用戶組', redirect: { name: 'UserGroupList' }, children: [ { path: 'list', name: 'UserGroupList', label: '用戶組列表' }, { path: 'config', name: 'UserGroupConfig', label: '用戶組設置' } ] } ] }, { path: '/setting', name: 'Setting', label: '系統設置' }, { path: '/login', name: 'Login', label: '登錄' }]const router = new VueRouter({ routes})export default router

其中前兩級路由會顯示在側邊欄中,第三級就不會顯示在側邊欄中了。

頁面內操作的權限設置不需要考慮很多其他東西,我們主要針對側邊欄以及路由進行問題的分析,通過分析,主要有以下幾個問題:

  1. 什么時候獲取 permissionList,如何存儲 permissionList
  2. 子路由全都沒權限時不應該顯示本身(例:當用戶列表和用戶組都沒有權限時,用戶也不應該顯示在側邊欄)
  3. 默認重定向的路由沒有權限時,應尋找 children 中有權限的一項重定向(例:用戶路由重定向到用戶列表路由,若用戶列表沒有權限,則應該重定向到用戶組路由)
  4. 當用戶直接輸入沒有權限的 url 時需要跳轉到沒有權限的頁面或其他操作。(路由限制)

下面我們針對以上問題一個一個解決。

什么時候獲取權限,存儲在哪 & 路由限制

我這里是在 router 的 beforeEach 中獲取的,獲取的 permissionList 是存放在 vuex 中。

原因是考慮到要做路由的限制,以及方便后面項目中對權限列表的使用,以下是實現的示例:

首先我們加入權限配置到 router 上:

// 以下只展示部分配置{ path: '/user', name: 'User', label: '用戶', meta: { permissions: ['U_1'] }, redirect: { name: 'UserList' }, children: [ { path: 'list', name: 'UserList', label: '用戶列表', meta: { permissions: ['U_1_1'] } }, { path: 'group', name: 'UserGroup', label: '用戶組', meta: { permissions: ['U_1_2'] }, redirect: { name: 'UserGroupList' }, children: [ { path: 'list', name: 'UserGroupList', label: '用戶組列表', meta: { permissions: ['U_1_2_1'] } }, { path: 'config', name: 'UserGroupConfig', label: '用戶組設置', meta: { permissions: ['U_1_2_2'] } } ] } ]}

可以看到我們把權限加在了 meta 上,是為了更簡單的從 router.beforeEch 中進行權限判斷,權限設置為一個數組,是因為一個頁面可能涉及多個權限。

接下來我們設置 router.beforeEach :

// 引入項目的 vueximport store from '@/store'// 引入判斷是否擁有權限的函數import { includePermission } from '@/utils/permission'router.beforeEach(async (to, from, next) => { // 先判斷是否為登錄,登錄了才能獲取到權限,怎么判斷登錄就不寫了 if (!isLogin) { try { // 這里獲取 permissionList await store.dispatch('getPermissionList') // 這里判斷當前頁面是否有權限 const { permissions } = to.meta if (permissions) { const hasPermission = includePermission(permissions) if (!hasPermission) next({ name: 'NoPermission' }) } next() } } else { next({ name: 'Login' }) }})

我們可以看到我們需要一個判斷權限的方法 & vuex 中的 getPermissionList 如下:

// @/storeexport default { state: { permissionList: [] }, mutations: { updatePermissionList: (state, payload) => { state.permissionList = payload } }, actions: { getPermissionList: async ({ state, commit }) => { // 這里是為了防止重復獲取 if (state.permissionList.length) return // 發送請求方法省略 const list = await api.getPermissionList() commit('updatePermissionList', list) } }}
// @/utils/permissionimport store from '@/store'/** * 判斷是否擁有權限 * @param {Array<string>} permissions - 要判斷的權限列表 */function includePermission (permissions = []) { // 這里要判斷的權限沒有設置的話,就等于不需要權限,直接返回 true if (!permissions.length) return true const permissionList = store.state.permissionList return !!permissions.find(permission => permissionList.includes(permission))}

重定向問題

以上我們解決了路由的基本配置與權限如何獲取,怎么限制路由跳轉,接下來我們要處理的就是重定向問題了。
這一點可能和我們項目本身架構有關,我們項目的側邊欄下還有子級,是以下圖中的 tab 切換展現的,正常情況當點擊藥品管理后頁面會重定向到入庫管理的 tab 切換頁面,但當入庫管理沒有權限時,則應該直接重定向到出庫管理界面。

所以想實現以上的效果,我需要重寫 router 的 redirect,做到可以動態判斷(因為在我配置路由時并不知道當前用戶的權限列表)

然后我查看了 vue-router 的文檔,發現了 redirect 可以是一個方法,這樣就可以解決重定向問題了。

vue-router 中 redirect 說明 ,根據說明我們可以改寫 redirect 如下:

// 我們需要引入判斷權限方法import { includePermission } from '@/utils/permission'const children = [ { path: 'list', name: 'UserList', label: '用戶列表', meta: { permissions: ['U_1_1'] } }, { path: 'group', name: 'UserGroup', label: '用戶組', meta: { permissions: ['U_1_2'] } }]const routeDemo = { path: '/user', name: 'User', label: '用戶', redirect: (to) => { if (includePermission(children[0].meta.permissions)) return { name: children[0].name } if (includePermission(children[1].meta.permissions)) return { name: children[1].name } }, children}

雖然問題解決了,但是發現這樣寫下去很麻煩,還要修改 router 的配置,所以我們使用一個方法生成:

// @/utils/permission/** * 創建重定向函數 * @param {Object} redirect - 重定向對象 * @param {string} redirect.name - 重定向的組件名稱 * @param {Array<any>} children - 子列表 */function createRedirectFn (redirect = {}, children = []) { // 避免緩存太大,只保留 children 的 name 和 permissions const permissionChildren = children.map(({ name = '', meta: { permissions = [] } = {} }) => ({ name, permissions })) return function (to) { // 這里一定不能在 return 的函數外面篩選,因為權限是異步獲取的 const hasPermissionChildren = permissionChildren.filter(item => includePermission(item.permissions)) // 默認填寫的重定向的 name const defaultName = redirect.name || '' // 如果默認重定向沒有權限,則從 children 中選擇第一個有權限的路由做重定向 const firstPermissionName = (hasPermissionChildren[0] || { name: '' }).name // 判斷是否需要修改默認的重定向 const saveDefaultName = !!hasPermissionChildren.find(item => item.name === defaultName && defaultName) if (saveDefaultName) return { name: defaultName } else return firstPermissionName ? { name: firstPermissionName } : redirect }}

然后我們就可以改寫為:

// 我們需要引入判斷權限方法import { includePermission, createRedirectFn } from '@/utils/permission'const children = [ { path: 'list', name: 'UserList', label: '用戶列表', meta: { permissions: ['U_1_1'] } }, { path: 'group', name: 'UserGroup', label: '用戶組', meta: { permissions: ['U_1_2'] } }]const routeDemo = { path: '/user', name: 'User', label: '用戶', redirect: createRedirectFn({ name: 'UserList' }, children), children}

這樣稍微簡潔一些,但我還是需要一個一個路由去修改,所以我又寫了一個方法來遞歸 router 配置,并重寫他們的 redirect:

// @/utils/permission/** * 創建有權限的路由配置(多級) * @param {Object} config - 路由配置對象 * @param {Object} config.redirect - 必須是 children 中的一個,并且使用 name */function createPermissionRouter ({ redirect, children = [], ...others }) { const needRecursion = !!children.length if (needRecursion) { return { ...others, redirect: createRedirectFn(redirect, children), children: children.map(item => createPermissionRouter(item)) } } else { return { ...others, redirect } }}

這樣我們只需要在最外層的 router 配置加上這樣一層函數就可以了:

import { createPermissionRouter } from '@/utils/permission'const routesConfig = [ { path: '/user', name: 'User', label: '用戶', meta: { permissions: ['U_1'] }, redirect: { name: 'UserList' }, children: [ { path: 'list', name: 'UserList', label: '用戶列表', meta: {  permissions: ['U_1_1'] } }, { path: 'group', name: 'UserGroup', label: '用戶組', meta: {  permissions: ['U_1_2'] }, redirect: { name: 'UserGroupList' }, children: [  {  path: 'list',  name: 'UserGroupList',  label: '用戶組列表',  meta: {  permissions: ['U_1_2_1']  }  },  {  path: 'config',  name: 'UserGroupConfig',  label: '用戶組設置',  meta: {  permissions: ['U_1_2_2']  }  } ] } ] }]export const routes = routesConfig.map(item => createPermissionRouter(item))const router = new VueRouter({ routes})export default router

當然這樣寫還有一個好處,其實你并不需要設置 redirect,這樣會自動重定向到 children 的第一個有權限的路由

側邊欄顯示問題

我們的項目使用的是根據路由的配置來生成側邊欄的,當然會加一些其他的參數來顯示顯示層級等問題,這里就不寫具體代碼了,如何解決側邊欄 children 全都無權限不顯示的問題呢。

這里我的思路是,把路由的配置也一同更新到 vuex 中,然后側邊欄配置從 vuex 中的配置來讀取。

由于這個地方涉及修改的東西有點多,而且涉及業務,我就不把代碼拿出來了,你可以自行實驗。

方便團隊部署權限點的方法

以上我們解決了大部分權限的問題,那么還有很多涉及到業務邏輯的權限點的部署,所以為了團隊中其他人可以優雅簡單的部署權限點到各個頁面中,我在項目中提供了以下幾種方式來部署權限:

通過指令 v-permission 來直接在 template 上設置

<div v-permission="['U_1']"></div>

通過全局方法 this.$permission 判斷,因為有些權限并非在模版中的

{ hasPermission () { // 通過方法 $permission 判斷是否擁有權限 return this.$permission(['U_1_1', 'U_1_2']) }}

這里要注意,為了 $permission 方法的返回值是可被監測的,判斷時需要從 this.$store 中來判斷,以下為實現代碼:

// @/utils/permission/** * 判斷是否擁有權限 * @param {Array<string|number>} permissions - 要判斷的權限列表 * @param {Object} permissionList - 傳入 store 中的權限列表以實現數據可監測 */function includePermissionWithStore (permissions = [], permissionList = []) { if (!permissions.length) return true return !!permissions.find(permission => permissionList.includes(permission))}
import { includePermissionWithStore } from '@/utils/permission'export default { install (Vue, options) { Vue.prototype.$permission = function (permissions) { const permissionList = this.$store.state.permissionList return includePermissionWithStore(permissions, permissionList) } }}

以下為指令的實現代碼(為了不與 v-if 沖突,這里控制顯示隱藏通過添加/移除 className 的方式):

// @/directive/permissionimport { includePermission } from '@/utils/permission'const permissionHandle = (el, binding) => { const permissions = binding.value if (!includePermission(permissions)) { el.classList.add('hide') } else { el.classList.remove('hide') }}export default { inserted: permissionHandle, update: permissionHandle}

總結

針對之前的問題,有以下的總結:

1、什么時候獲取 permissionList,如何存儲 permissionList

router.beforeEach 獲取,存儲在 vuex。

2、子路由全都沒權限時不應該顯示本身(例:當用戶列表和用戶設置都沒有權限時,用戶也不應該顯示在側邊欄)

通過存儲路由配置到 vuex 中,生成側邊欄設置,獲取權限后修改 vuex 中的配置控制顯示 & 隱藏。

3、默認重定向的路由沒有權限時,應尋找 children 中有權限的一項重定向(例:用戶路由重定向到用戶列表路由,若用戶列表沒有權限,則應該重定向到用戶組路由)

通過 vue-router 中 redirect 設置為 Function 來實現

4、當用戶直接輸入沒有權限的 url 時需要跳轉到沒有權限的頁面或其他操作。(路由限制)

在 meta 中設置權限, router.beforeEach 中判斷權限。

以上就是我對于這次權限需求的大體解決思路與代碼實現,可能并不是很完美,但還是希望可以幫助到你 ^_^

好了,以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對武林網的支持。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 中山市| 师宗县| 广饶县| 凤台县| 靖宇县| 雷山县| 呼和浩特市| 洞头县| 靖州| 宿迁市| 蚌埠市| 萝北县| 抚顺县| 靖宇县| 康定县| 大余县| 舞钢市| 丰城市| 揭西县| 长武县| 洪泽县| 辰溪县| 镇宁| 漠河县| 呼伦贝尔市| 柳州市| 普洱| 凤凰县| 潢川县| 海口市| 周宁县| 黄山市| 正定县| 宜川县| 锡林浩特市| 铅山县| 佳木斯市| 锦屏县| 定西市| 赤峰市| 禄丰县|