vue后臺(tái)管理系統(tǒng)中的權(quán)限控制

0.背景和目的

后臺(tái)管理系統(tǒng)一般都少不了登錄權(quán)限控制璧微,這里我們討論以下如何實(shí)現(xiàn)權(quán)限控制嗦玖。

1.主要概念

一個(gè)完整的權(quán)限控制模塊涉及較多,一般包括用戶管理妈经、角色管理淮野、資源管理(即菜單管理,當(dāng)前系統(tǒng)總共有哪些菜單)吹泡。

用戶

這個(gè)沒什么好說的骤星,就是你登錄時(shí)候的賬號(hào),用戶有自己的信息爆哑,比如姓名洞难,賬號(hào)密碼,手機(jī)號(hào)等揭朝,用戶登錄之后會(huì)拿到用于區(qū)別用戶身份的token队贱,一般token會(huì)放在請(qǐng)求的接口的header里面,供后端來確定當(dāng)前用戶潭袱。

一個(gè)用戶可以屬于一個(gè)或者多個(gè)角色露筒,用戶會(huì)獲得角色下的權(quán)限。

角色

角色可以理解為具有某些權(quán)限的一類用戶敌卓,比如管理員角色慎式,用于維護(hù)整個(gè)系統(tǒng),具有最高權(quán)限,普通操作人員可能只具有某些業(yè)務(wù)的普通操作權(quán)限瘪吏。

權(quán)限菜單

權(quán)限菜單可以理解為資源癣防,比如當(dāng)前有哪些菜單,哪些功能掌眠,功能下面有哪些按鈕等蕾盯。

權(quán)限有不同的粒度,比如有的系統(tǒng)相對(duì)簡(jiǎn)單只需要控制到具體頁(yè)面即可蓝丙,不再具體控制該頁(yè)面下的具體按鈕级遭。

還有更進(jìn)一步,除了菜單按鈕之外渺尘,還可以控制到具體業(yè)務(wù)數(shù)據(jù)挫鸽。

本篇只討論控制到菜單和按鈕級(jí)別。

對(duì)應(yīng)關(guān)系圖

2.核心流程

從流程上來說用戶權(quán)限包括兩部分:設(shè)定權(quán)限鸥跟、拉取權(quán)限

設(shè)定權(quán)限

設(shè)定系統(tǒng)菜單

即當(dāng)前系統(tǒng)的功能菜單丢郊、按鈕管理,可以做成一個(gè)功能医咨,如果系統(tǒng)簡(jiǎn)單也可以手動(dòng)編輯數(shù)據(jù)庫(kù)實(shí)現(xiàn)枫匾。

設(shè)定角色下的權(quán)限

由用戶來勾選角色下包括的菜單、按鈕拟淮。

設(shè)定用戶所屬角色

用戶可以屬于一個(gè)或幾個(gè)角色干茉,用戶的權(quán)限是所屬的角色最大集合


拉取權(quán)限

這里我們可以在全局狀態(tài)管理(Vuex)設(shè)置一個(gè)狀態(tài)標(biāo)識(shí)(hasLoadAccess)和一個(gè)獲取權(quán)限動(dòng)作(loadAccess)

早期的時(shí)候動(dòng)態(tài)生成路由并不現(xiàn)實(shí),但當(dāng)vue-router增加了addRouters方法之后很泊,這一切變得簡(jiǎn)單了角虫。

當(dāng)用戶拉取權(quán)限之后,根據(jù)得到的數(shù)據(jù)生成路由并addRouters和生成導(dǎo)航菜單撑蚌。

3.服務(wù)端設(shè)計(jì)

數(shù)據(jù)庫(kù)表設(shè)計(jì)

menu表

用于存儲(chǔ)系統(tǒng)中需要權(quán)限驗(yàn)證的路由

id? ? int? 主鍵自增

name String 菜單功能名稱

index String? 標(biāo)識(shí)上遥,用戶匹配本地組件

parentId int 菜單所屬,如果是一級(jí)菜單值為0

isMenu int? ?是否是菜單

這里需要特別注意争涌,因?yàn)橛行┞酚蓪儆诓藛畏鄢行┞酚刹粚儆诓藛危热缬脩艄芾眄?yè)面是需要顯示在導(dǎo)航菜單中亮垫,但用戶新增模软、用戶編輯不應(yīng)該出現(xiàn)在導(dǎo)航菜單中,這里我們都當(dāng)作路由處理饮潦,有一個(gè)好處就是用戶刷新可以停留在當(dāng)前頁(yè)面燃异,邏輯相對(duì)簡(jiǎn)單,當(dāng)然也可以不使用路由來處理這種編輯的頁(yè)面继蜡,比如通過對(duì)話框回俐、抽屜等逛腿,根據(jù)實(shí)際交互設(shè)計(jì)選擇

icon String 結(jié)合iconfront 相應(yīng)圖標(biāo)的類名

container String 容器組件標(biāo)識(shí),這里我們討論二層路由仅颇,container為第一層組件

path String 路由单默,絕對(duì)路徑

sort int 排序,用于控制菜單的順序忘瓦,可以手動(dòng)修改

按鈕表

存儲(chǔ)系統(tǒng)中的所有按鈕

id int 主鍵

uuid string 唯一標(biāo)識(shí)

name String 按鈕名稱

parentId int 所屬菜單的id

角色表

id int 主鍵id

name String 角色名稱

sort int? 順序

角色權(quán)限表

id int 主鍵id

roleId int 角色I(xiàn)d

menuId int 菜單Id

用戶表

id int 主鍵id

name String 用戶姓名

username String 用戶名

password String 加密碼存儲(chǔ)的密碼

phone string 電話號(hào)碼 不是必須有的字段搁廓,根據(jù)業(yè)務(wù)需求設(shè)定

用戶角色關(guān)系表

id int? 主鍵

userId int? 用戶id

roleId int 角色id

權(quán)限控制少不了后端的配合,除了對(duì)角色耕皮,用戶等的增刪改查外境蜕,還需要提供一個(gè)接口getAccess返回用戶擁有的權(quán)限,返回結(jié)構(gòu)大致如下:

{

code: 0,

????data: [

{

id: 1

index: 'user-management',

name: '用戶管理',

icon: 'icon icon-user',

refer: '/user-management',

? ? ? ? ? ? children: [

{

index:? '',

name: '',

refer: '',

isMenu: true

????????????????}

...

????????????],

...

????????}

????]

}

4.本地路由組織

存儲(chǔ)本地所有路由組件

export default {

? ? 'console': import('path/console.vue'), // 根容器

? ?'index':import('path/index.vue'), //? 以鍵值對(duì)的形式凌停,鍵為數(shù)據(jù)庫(kù)中的index? 根據(jù)index進(jìn)行匹配粱年,值為組件,這里使用import的方式

? ?'user-management': import('path/index.vue')

}

5.權(quán)限獲取

結(jié)合vue-router苦锨,我們可以在路由守衛(wèi)的鉤子函數(shù)beforeEach中執(zhí)行動(dòng)作逼泣,偽代碼如下:

import router from 'vue-router'

import Store from 'vuex'

const whiteList = [? 'login' ]? //不需要權(quán)限的路由

const token = Cookies.get('token')

...

router.beforeEach((to, from, next) => {

? ? if(whiteList.indexOf(to.name) > -1)? {? // 跳轉(zhuǎn)的是不需要登錄狀態(tài)的路由? 直接放行

? ? ? ? next()

????} else if(!token) { // 需要權(quán)限? 但是token不存在? 直接跳轉(zhuǎn)到登錄頁(yè)面

? ? ? ? router.push({path: '/login'})

????} else if(!Store.hasLoadAccess) {? //需要權(quán)限 且token 但沒有還沒有獲取權(quán)限? 則開始獲取權(quán)限

? ? ? ? Strore.loadAccess()

????} else {? // 其余直接跳轉(zhuǎn)到相應(yīng)路由? 這里一定要調(diào)用next 函數(shù)趴泌,不然不能跳轉(zhuǎn)

? ? ? ? next()

????}

})

// 拉取權(quán)限動(dòng)作舟舒,此處在vuex模塊access中

export default {

? ? state: {

? ? ? ? hasLoadAccess: false,

? ? ? ? accessTree: []

????},

mutiation:{

? ? ? ? setLoadStatus(state, status){

? ? ? ? ? ? state.hasLoadAccess = status

? ? ? ? }

}

action: {

? ? ? ? async loadAccess(){

? ? ? ? ? ? const res = await getAccess()

? ? ? ? ? ? if(res.success){

? ? ? ? ? ? ? ? ? ? // 處理路由

????????????}

????????}

????}

}

6.動(dòng)態(tài)生成路由和導(dǎo)航菜單

生成路由

根據(jù)后臺(tái)服務(wù)返回,生成路由數(shù)組嗜憔,路由對(duì)應(yīng)的組件從resourceMap中根據(jù)index進(jìn)行匹配秃励。

并不是所有路由都需要根據(jù)根據(jù)后端數(shù)據(jù)來生成,這里主要是指不需要用戶權(quán)限驗(yàn)證的頁(yè)面吉捶,如登錄頁(yè)面夺鲜、找回密碼等頁(yè)面,這些路由可以寫在前端初始路由里面呐舔。

生成菜單

生成菜單可以放在導(dǎo)航菜單的計(jì)算屬性中币励,或者直接vuex 中的getter中

7.用戶管理、角色管理珊拼、資源管理

此處為正常的增刪改茶沒有需要特別注意的內(nèi)容

8.優(yōu)化

按照以上邏輯即可實(shí)現(xiàn)權(quán)限控制食呻,但是還有以下幾個(gè)問題需要處理

1.當(dāng)跳轉(zhuǎn)到不是菜單對(duì)應(yīng)的路由時(shí),菜單缺少高亮狀態(tài)

可以在menu表中增加refer字段澎现,并存儲(chǔ)到meta中仅胞,設(shè)定當(dāng)前路由應(yīng)該高亮的路由,比如用戶管理頁(yè)面的refer為‘/user-management’,即本身的path剑辫,用戶新增干旧、編輯頁(yè)面的refer也為‘/user-management’

el-menu 組件的default-active設(shè)置為router.meta.refer

2.默認(rèn)路由問題,一般登錄后的默認(rèn)主頁(yè)是home頁(yè)面妹蔽,但如果在菜單設(shè)置的時(shí)候用戶不勾選home頁(yè)面如何處理椎眯。

有兩種處理方式:

1挠将、設(shè)置角色權(quán)限時(shí)home默認(rèn)為選中狀態(tài)且不允許取消選中。

2编整、允許不選擇home菜單捐名,當(dāng)用戶沒有home頁(yè)權(quán)限時(shí),自動(dòng)生成一個(gè)home頁(yè)面闹击,即不包含任何數(shù)據(jù)的歡迎頁(yè)面镶蹋。

3.后端接口過濾問題

這些過濾只是在前端對(duì)用戶的動(dòng)作進(jìn)行限制,但是防君子不防小人赏半,因?yàn)橛脩敉耆梢岳@過前端的這些限制贺归,直接利用postman請(qǐng)求接口,所以權(quán)限限制最終還要后端處理断箫,進(jìn)行用戶驗(yàn)證拂酣。不過這是后端同學(xué)的工作,我們不做討論仲义。

4.資源管理問題

資源管理也就是我們的菜單婶熬、按鈕的管理,如果產(chǎn)品在不斷的迭代埃撵,需要頻繁修改菜單赵颅,或者想把產(chǎn)品做的更晚上,需要一個(gè)資源管理功能暂刘,通過接口來處理菜單邏輯饺谬。

如果資源管理頻率很低,也可以手動(dòng)修改數(shù)據(jù)庫(kù)谣拣,但需要注意的是手動(dòng)修改menu表募寨,不會(huì)處理角色的邏輯關(guān)系,當(dāng)新增或者刪除菜單時(shí)森缠,需要重新保存角色權(quán)限拔鹰,才會(huì)更新角色權(quán)限。

9.結(jié)語(yǔ)

以上是對(duì)vue項(xiàng)目中權(quán)限控制的一種簡(jiǎn)單實(shí)現(xiàn)贵涵,適合小型系統(tǒng)列肢,如果對(duì)于多系統(tǒng)統(tǒng)一認(rèn)證的系統(tǒng),本篇文章的后端部分并不適用独悴,前端部分可以借鑒參考例书。

原創(chuàng)不易,如果本篇文章對(duì)您有所幫助刻炒,還請(qǐng)點(diǎn)贊支持决采。如果您有疑問或者建議,請(qǐng)留言坟奥,我會(huì)在方便的時(shí)候做解答树瞭。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拇厢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子晒喷,更是在濱河造成了極大的恐慌孝偎,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凉敲,死亡現(xiàn)場(chǎng)離奇詭異衣盾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)爷抓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門势决,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蓝撇,你說我怎么就攤上這事果复。” “怎么了渤昌?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵虽抄,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我独柑,道長(zhǎng)迈窟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任群嗤,我火速辦了婚禮菠隆,結(jié)果婚禮上兵琳,老公的妹妹穿的比我還像新娘狂秘。我一直安慰自己,他們只是感情好躯肌,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布者春。 她就那樣靜靜地躺著,像睡著了一般清女。 火紅的嫁衣襯著肌膚如雪钱烟。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天嫡丙,我揣著相機(jī)與錄音拴袭,去河邊找鬼。 笑死曙博,一個(gè)胖子當(dāng)著我的面吹牛拥刻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播父泳,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼般哼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼吴汪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起蒸眠,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤漾橙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后楞卡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體霜运,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年蒋腮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了觉渴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡徽惋,死狀恐怖案淋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情险绘,我是刑警寧澤踢京,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站宦棺,受9級(jí)特大地震影響瓣距,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜代咸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一蹈丸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧呐芥,春花似錦逻杖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至滨攻,卻和暖如春够话,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背光绕。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工女嘲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人诞帐。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓欣尼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親景埃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子媒至,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容