這篇文章將為大家詳細(xì)講解有關(guān)如何使用Vue實現(xiàn)按鈕級權(quán)限方案,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
創(chuàng)新互聯(lián)專注于晉源企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城網(wǎng)站制作。晉源網(wǎng)站建設(shè)公司,為晉源等地區(qū)提供建站服務(wù)。全流程按需求定制開發(fā),專業(yè)設(shè)計,全程項目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
演示
在年初開發(fā)一個中后臺管理系統(tǒng),功能涉及到了各個部門(產(chǎn)品、客服、市場等等),在開始的版本中,我和后端配合使用了花褲衩手摸手系列的權(quán)限方案,前期非常nice,但是慢慢的隨著功能增多、業(yè)務(wù)越來越復(fù)雜,就變得有些吃力了,因為我們的權(quán)限動態(tài)性太大了
手摸手系列權(quán)限方案是有比較清晰的權(quán)限劃分的,而我們公司部門的崗位職責(zé)有時比較模糊。
后端采用RBAC權(quán)限方案,為了達(dá)到第1點要求,將角色劃分的很細(xì),并且角色有時頻繁變動,導(dǎo)致每一次前端都需要手動維護(hù)
為了解決上面2個痛點,我將原方案進(jìn)行了一丟丟改造。
前端不再以角色來控制權(quán)限,而是以更小粒度的操作(接口)來控制,也就是前端不關(guān)心角色
路由還是由前端維護(hù)(我們的后端很排斥維護(hù)和他們不相干的東西:joy:),但改為通過操作列表對權(quán)限路由進(jìn)行過濾
使用單一的方式(方便維護(hù))控制頁面的局部權(quán)限,不再使用自定義指令方式,而是通過函數(shù)式組件,原因是使用自定義指令有多余的開銷(插入再移除)
后端的配合:
routerName
有一些注意點:
比如一個有權(quán)限的列表頁面A,同時這個列表接口被權(quán)限頁面B使用,現(xiàn)在你配置權(quán)限讓某一個用戶沒有A頁面權(quán)限,但可以使用B頁面,如果你的本意是可以使用B頁面的所有功能,這時就會有問題,所以盡量不要將權(quán)限接口跨頁面使用,需要分清哪些數(shù)據(jù)需要通過字典接口獲取還是通過權(quán)限接口獲取
有些人可能會糾結(jié),前端維護(hù)權(quán)限安全嗎?肯定是不安全的,安全性主要還在后端這邊把控,后端做好數(shù)據(jù)和接口方面的權(quán)限控制,前端做權(quán)限控制我認(rèn)為主要還是為了交互體驗等。沒有權(quán)限你為什么要讓我看到那一坨?
在使用這種方式之前,要明確當(dāng)前場景是否確實需要這么做,畢竟在項目比較大且接口很多的情況下,你跟操作碼之間有一場持久戰(zhàn)
實現(xiàn)
操作列表示例
以Restful風(fēng)格接口為例
const operations = [ { url: '/xxx', type: 'get', name: '查詢xxx', routeName: 'route1', // 接口對應(yīng)的路由 opcode: 'XXX_GET' // 操作碼,不變的 }, { url: '/xxx', type: 'post', name: '新增xxx', routeName: 'route1', opcode: 'XXX_POST' }, // ...... ]
路由的變化
在路由的 meta 中增加一個配置字段如 requireOps ,值可能為 String 或者 Array ,這表示當(dāng)前路由頁面要顯示的必要的操作碼, Array 類型是為了處理一個路由頁面需要滿足同時存在多個操作權(quán)限時才顯示的情況。若值不為這2種則視為無權(quán)限控制,任何用戶都能訪問
由于最終需要根據(jù)過濾后的權(quán)限路由動態(tài)生成菜單,所以還需要在路由選項中增加幾個字段處理顯示問題,其中 hidden 優(yōu)先級大于 visible
hidden visible const permissionRoutes = [ { // visible: false, // hidden: true, path: '/xxx', name: 'route1', meta: { title: '路由1', requireOps: 'XXX_GET' }, // ... } ]
由于路由在前端維護(hù),所以以上配置只能寫死,如果后端能同意維護(hù)這一份路由表,那就可以有很多的發(fā)揮空間了,體驗也能做的更好。
權(quán)限路由過濾
先將權(quán)限路由規(guī)范一下,同時保留一個副本,可能在可視化時需要用到
const routeMap = (routes, cb) => routes.map(route => { if (route.children && route.children.length > 0) { route.children = routeMap(route.children, cb) } return cb(route) }) const hasRequireOps = ops => Array.isArray(ops) || typeof ops === 'string' const normalizeRequireOps = ops => hasRequireOps(ops) ? [].concat(...[ops]) : null const normalizeRouteMeta = route => { const meta = route.meta = { ...(route.meta || {}) } meta.requireOps = normalizeRequireOps(meta.requireOps) return route } permissionRoutes = routeMap(permissionRoutes, normalizeRouteMeta) const permissionRoutesCopy = JSON.parse(JSON.stringify(permissionRoutes))
獲取到操作列表后,只需要遍歷權(quán)限路由,然后查詢 requireOps 代表的操作有沒有在操作列表中。這里需要處理一下 requireOps 未設(shè)置的情況,如果子路由中都是權(quán)限路由,需要為父級路由自動加上 requireOps 值,不然當(dāng)所有子路由都沒有權(quán)限時,父級路由就被認(rèn)為是無權(quán)限控制且可訪問的;而如果子路由中只要有一個路由無權(quán)限控制,那就不需要處理父路由。所以這里可以用遞歸來解決,先處理子路由再處理父路由
const filterPermissionRoutes = (routes, cb) => { // 可能父路由沒有設(shè)置requireOps 需要根據(jù)子路由確定父路由的requireOps routes.forEach(route => { if (route.children) { route.children = filterPermissionRoutes(route.children, cb) if (!route.meta.requireOps) { const hasNoPermission = route.children.some(child => child.meta.requireOps === null) // 如果子路由中存在不需要權(quán)限控制的路由,則跳過 if (!hasNoPermission) { route.meta.requireOps = [].concat(...route.children.map(child => child.meta.requireOps)) } } } }) return cb(routes) }
然后根據(jù)操作列表對權(quán)限路由進(jìn)行過濾
let operations = null // 從后端獲取后更新它 const hasOp = opcode => operations ? operations.some(op => op.opcode === opcode) : false const proutes = filterPermissionRoutes(permissionRoutes, routes => routes.filter(route => { const requireOps = route.meta.requireOps if (requireOps) { return requireOps.some(hasOp) } return true })) // 動態(tài)添加路由 router.addRoutes(proutes)
函數(shù)式組件控制局部權(quán)限
這個組件實現(xiàn)很簡單,根據(jù)傳入的操作碼進(jìn)行權(quán)限判斷,若通過則返回插槽內(nèi)容,否則返回null。另外,為了統(tǒng)一風(fēng)格,支持一下 root 屬性,表示組件的根節(jié)點
const AccessControl = { functional: true, render (h, { data, children }) { const attrs = data.attrs || {} // 如果是root,直接透傳 if (attrs.root !== undefined) { return h(attrs.root || 'div', data, children) } if (!attrs.opcode) { return h('span', { style: { color: 'red', fontSize: '30px' } }, '請配置操作碼') } const opcodes = attrs.opcode.split(',') if (opcodes.some(hasOp)) { return children } return null } }
動態(tài)生成權(quán)限菜單
以ElementUI為例,由于動態(tài)渲染需要進(jìn)行遞歸,如果以文件組件的形式會多一層根組件,所以這里直接用render function簡單寫一個示例,可以根據(jù)自己的需求改造
// 權(quán)限菜單組件 export const PermissionMenuTree = { name: 'MenuTree', props: { routes: { type: Array, required: true }, collapse: Boolean }, render (h) { const createMenuTree = (routes, parentPath = '') => routes.map(route => { // hidden: 為true時當(dāng)前菜單和子菜單都不顯示 if (route.hidden === true) { return null } // 子路徑處理 const fullPath = route.path.charAt(0) === '/' ? route.path : `${parentPath}/${route.path}` // visible: 為false時不顯示當(dāng)前菜單,但顯示子菜單 if (route.visible === false) { return createMenuTree(route.children, fullPath) } const title = route.meta.title const props = { index: fullPath, key: route.path } if (!route.children || route.children.length === 0) { return h( 'el-menu-item', { props }, [h('span', title)] ) } return h( 'el-submenu', { props }, [ h('span', { slot: 'title' }, title), ...createMenuTree(route.children, fullPath) ] ) }) return h( 'el-menu', { props: { collapse: this.collapse, router: true, defaultActive: this.$route.path } }, createMenuTree(this.routes) ) } }
接口的權(quán)限控制
我們一般用axios,這里只需要在axios封裝的基礎(chǔ)上加幾行代碼就可以了,axios封裝花樣多多,這里簡單示例
const ajax = axios.create(/* config */) export default { post (url, data, opcode, config = {}) { if (opcode && !hasOp(opcode)) { return Promise.reject(new Error('沒有操作權(quán)限')) } return ajax.post(url, data, { /* config */ ...config }).then(({ data }) => data) }, // ... }
關(guān)于“如何使用Vue實現(xiàn)按鈕級權(quán)限方案”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
文章名稱:如何使用Vue實現(xiàn)按鈕級權(quán)限方案
文章網(wǎng)址:http://www.rwnh.cn/article44/gjhiee.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App開發(fā)、手機網(wǎng)站建設(shè)、云服務(wù)器、虛擬主機、全網(wǎng)營銷推廣、域名注冊
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)