前端權(quán)限控制和管理實(shí)現(xiàn)步驟詳解
1.前言
在Web系統(tǒng)中,一直以來(lái)權(quán)限都只是后端程序所控制的。因?yàn)閃eb 系統(tǒng)圍繞的是數(shù)據(jù),而和數(shù)據(jù)庫(kù)最緊密接觸的是后端程序。所以在很長(zhǎng)的一段時(shí)間內(nèi),權(quán)限一直都只是后端程序要考慮的話題。 但是隨著前后端分離架構(gòu)的流行,越來(lái)越多的項(xiàng)目也在前端進(jìn)行權(quán)限控制。
2.權(quán)限相關(guān)概念
2.1權(quán)限的分類(lèi)
(1)后端權(quán)限
從根本上講前端僅僅只是視圖層的展示,權(quán)限的核心是在于服務(wù)器中的數(shù)據(jù),所以后端才是權(quán)限的關(guān)鍵,后端權(quán)限可以控制某個(gè)用戶(hù)是否能夠查詢(xún)數(shù)據(jù),是否能夠修改數(shù)據(jù)等操作。
ps:后端如何知道該請(qǐng)求是哪個(gè)用戶(hù)發(fā)過(guò)來(lái)的
- cookie
- session
- token
ps:后端的權(quán)限設(shè)計(jì)RBAC(一般五張表)
- 用戶(hù)
- 角色
- 權(quán)限
- 還有兩張關(guān)系表
(2)前端權(quán)限
本質(zhì)上來(lái)說(shuō),前端權(quán)限的控制就是控制前端的視圖層的展示和前端所發(fā)送的請(qǐng)求。但是只有前端權(quán)限控制沒(méi)有后端權(quán)限控制是萬(wàn)萬(wàn)不可的。 前端權(quán)限控制只是達(dá)到錦上添花的效果。
2.2前端權(quán)限的意義
如果僅從能夠修改服務(wù)器中數(shù)據(jù)庫(kù)中的數(shù)據(jù)層面上講,確實(shí)只在后端做控制就足夠了,那為什么越來(lái)越多的項(xiàng)目也進(jìn)行了前端權(quán)限的控制,主要有這幾方面的好處。
- 降低非法操作的可能性;
- 不怕贓偷就怕賊惦記,在頁(yè)面中展示出一個(gè)就算點(diǎn)擊了也最終會(huì)失敗的按鈕,勢(shì)必會(huì)增加有心者非法操作的可能性;
- 盡可能排除不必要清求,減輕服務(wù)器壓力,沒(méi)必要的請(qǐng)求,操作失敗的清求,不具備權(quán)限的清求,這些應(yīng)該壓根就不需要發(fā)送,請(qǐng)求少了,自然也會(huì)減輕服務(wù)器的壓力;
- 提高用戶(hù)體驗(yàn);
- 根據(jù)用戶(hù)具備的權(quán)限為該用戶(hù)展現(xiàn)自己權(quán)限范圍內(nèi)的內(nèi)容,避免在界面上給用戶(hù)帶來(lái)困擾,讓用戶(hù)專(zhuān)注于分內(nèi)之事;
3.前端權(quán)限控制思路
3.1菜單的權(quán)限控制
在登錄請(qǐng)求中,會(huì)得到用戶(hù)的權(quán)限數(shù)據(jù),當(dāng)然,這個(gè)需要后端返回?cái)?shù)據(jù)的支持。前端根據(jù)權(quán)限數(shù)據(jù),展示對(duì)應(yīng)的菜單。點(diǎn)擊菜單,才能查看相關(guān)的界面。
3.2界面的權(quán)限控制
如果用戶(hù)沒(méi)有登錄,手動(dòng)在地址欄敲入登錄后主界面的地址,則需要跳轉(zhuǎn)到登錄界面。
如果用戶(hù)已經(jīng)登錄,如果手動(dòng)敲入非權(quán)限內(nèi)的地址,則需要跳轉(zhuǎn)404 界面。
3.3按鈕的權(quán)限控制
在某個(gè)菜單的界面中,還得根據(jù)權(quán)限數(shù)據(jù),展示出可進(jìn)行操作的按鈕,比如刪除、修改、增加等按鈕。
3.4接口的權(quán)限控制
如果用戶(hù)通過(guò)非常規(guī)操作,比如通過(guò)瀏覽器調(diào)試工具將某些禁用的按鈕變成啟用狀態(tài),此時(shí)發(fā)的請(qǐng)求也應(yīng)該被前端所攔截。
4.實(shí)現(xiàn)步驟
4.1菜單欄控制
用戶(hù)登錄之后,服務(wù)端返回一個(gè)數(shù)據(jù),這個(gè)數(shù)據(jù)有菜單列表和token,我們把這個(gè)數(shù)據(jù)放入到vuex中,然后主頁(yè)根據(jù)vuex中的數(shù)據(jù)進(jìn)行菜單列表的渲染。
問(wèn)題:刷新界面后vuex數(shù)據(jù)會(huì)消失,菜單欄會(huì)消失解決:將數(shù)據(jù)存儲(chǔ)在sessionStorage中,并讓其和vuex中的數(shù)據(jù)保持同步(用專(zhuān)門(mén)的持久化插件也可以)store/index.js:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
//每次點(diǎn)擊頁(yè)面中的刷新按鈕,state中的數(shù)據(jù)就會(huì)根據(jù)下面的數(shù)據(jù)重新初始化
rightList: JSON.parse(sessionStorage.getItem('rightList') || '[]'),
username: sessionStorage.getItem('username'),
},
mutations: {
setRightList(state, newData) {
state.rightList = newData
sessionStorage.setItem('rightList', JSON.stringify(newData)) //sessionStorage只能存儲(chǔ)字符串
},
setUsername(state, newData) {
state.username = newData
sessionStorage.setItem('username', newData)
},
},
actions: {},
getters: {},
})
4.2界面的控制
登錄成功后,將token數(shù)據(jù)存儲(chǔ)在sessionStorage中,用來(lái)判斷是否登錄
(1)路由導(dǎo)航守衛(wèi)
router/index.js:
router.beforeEach((to, from, next) => {
//頁(yè)面跳轉(zhuǎn)之前做攔截動(dòng)作,判斷token是否存在
if (to.path === '/login') next()
else {
const token = sessionStorage.getItem('token')
if (!token) next('/login')
else {
next()
}
}
})
問(wèn)題:這樣用戶(hù)在登錄之后就可以訪問(wèn)其他界面了,但如果用戶(hù)A登錄之后只能訪問(wèn)a頁(yè)面不能訪問(wèn)b頁(yè)面,但是這時(shí)候他還是可以通過(guò)地址欄輸入進(jìn)入到b頁(yè)面。
解決:當(dāng)然我們也可以設(shè)置路由導(dǎo)航守衛(wèi),但是如果有多個(gè)頁(yè)面,設(shè)置會(huì)非常不方便,并且對(duì)于用戶(hù)A來(lái)說(shuō),它是不用訪問(wèn)b頁(yè)面的,這時(shí)候我們可以對(duì)A不顯示b頁(yè)面,這個(gè)時(shí)候我們就用到了動(dòng)態(tài)路由。
(2)動(dòng)態(tài)路由
根據(jù)當(dāng)前用戶(hù)所擁有的的權(quán)限數(shù)據(jù)來(lái)動(dòng)態(tài)添加所需要的路由。
先定義好所有的路由規(guī)則:router/routerMap.js:
import Users from '@/components/user/Users.vue'
import Roles from '@/components/role/Roles.vue'
import GoodsCate from '@/components/goods/GoodsCate.vue'
import GoodsList from '@/components/goods/GoodsList.vue'
const userRule = { path: '/users', component: Users }
const roleRule = { path: '/roles', component: Roles }
const goodRule = { path: '/goods', component: GoodsList }
const categoryRule = { path: '/categories', component: GoodsCate }
const routesMap = {
users: userRule,
roles: roleRule,
goods: goodRule,
categories: categoryRule,
}
export { routesMap }
登錄成功之后動(dòng)態(tài)添加路由,注意這個(gè)initDynamicRoutes的方法需要暴露出去在登錄頁(yè)面調(diào)用


這樣當(dāng)用戶(hù)A在地址欄輸入自己不能訪問(wèn)的路由時(shí),就不會(huì)跳轉(zhuǎn)到該頁(yè)面,而是跳轉(zhuǎn)到404頁(yè)面。

問(wèn)題:如果我們重新刷新的話動(dòng)態(tài)路由就會(huì)消失,動(dòng)態(tài)路由是在登錄成功之后才會(huì)調(diào)用,刷新的時(shí)候并沒(méi)有調(diào)用,所以動(dòng)態(tài)路由沒(méi)有添加上。
解決:可以在app.vue中的created中調(diào)用添加動(dòng)態(tài)路由的方法

4.3按鈕的控制
雖然用戶(hù)可以看到某些界面了,但是對(duì)于這個(gè)界面的一些按鈕,該用戶(hù)可能是沒(méi)有權(quán)限的。 因此,我們需要對(duì)組件中的一些按鈕進(jìn)行控制,用戶(hù)不具備權(quán)限的按鈕就隱藏或者禁用,而在這塊的實(shí)現(xiàn)中,可以把該邏輯放到自定義指令中
比如我們可以根據(jù)后端返回的數(shù)據(jù)right來(lái)判斷用戶(hù)有什么權(quán)限,如下圖。

添加自定義指令 控制按鈕:
import Vue from 'vue'
import store from '../store';
import router from '../router';
const permission = {
inserted(el, binding) {
//傳過(guò)來(lái)的參數(shù)中,action表示被綁定的按鈕是什么操作;effect表示當(dāng)用戶(hù)沒(méi)有這個(gè)操作權(quán)限的時(shí)候,
//應(yīng)該如何如理這個(gè)按鈕
const action = binding.value.action
const effect = binding.value.effect
//獲取用戶(hù)在當(dāng)前路由中所具有的權(quán)限列表
let currentPathPermissions = router.currentRoute.meta
if(currentPathPermissions.indexOf(action) == -1){
if(effect === 'disabled'){
el.disabled = true
el.classList.add('is-disabled') //element-ui需要的處理
}
else
el.parentNode.removeChild(el)
//el.style.display = 'none'
}
}
}
Vue.directive('permission', permission)

4.4接口的控制
(1)請(qǐng)求控制
除了登錄請(qǐng)求都得要帶上token,這樣服務(wù)器才可以鑒別你的身份。這塊需要配置axios的請(qǐng)求攔截器。

如果發(fā)出了非權(quán)限內(nèi)的請(qǐng)求,應(yīng)該直接在前端范圍內(nèi)阻止,雖然這個(gè)請(qǐng)求發(fā)到服務(wù)器也會(huì)被拒絕。
非權(quán)限內(nèi)的請(qǐng)求:比如a用戶(hù)是不能夠操作該頁(yè)面的按鈕的,但是他通過(guò)f12調(diào)試把按鈕改為可點(diǎn)擊,如果我們不對(duì)這個(gè)請(qǐng)求進(jìn)行處理,那么這個(gè)請(qǐng)求就會(huì)發(fā)送出去。
//當(dāng)前模塊中具備的權(quán)限關(guān)系映射
const actionMapping = {
get: 'view', //查看-->get請(qǐng)求
post: 'add', //添加-->post請(qǐng)求
put: 'edit', //編輯-->put請(qǐng)求
delete: 'delete', //刪除-->delete請(qǐng)求
}
//請(qǐng)求攔截控制
axios.interceptors.request.use((request) => {
// console.log(request.url);
// console.log(request.method);
if (request.url !== 'login') {
//除了登錄請(qǐng)求以外,在請(qǐng)求頭中全部添加上token
request.headers.Authorization = sessionStorage.getItem('token')
//將請(qǐng)求方式映射成為操作類(lèi)型的權(quán)限
const action = actionMapping[request.method]
//獲取用戶(hù)在當(dāng)前路由下所具有的權(quán)限
const currentRight = router.currentRoute.meta
if (currentRight && currentRight.indexOf(action) === -1) {
//沒(méi)有權(quán)限發(fā)送請(qǐng)求,通過(guò)報(bào)錯(cuò)攔截
alert('沒(méi)有權(quán)限!')
return Promise.reject(new Error('沒(méi)有權(quán)限'))
}
}
return request
})
(2)響應(yīng)控制
得到了服務(wù)器返回的狀態(tài)碼401,代表token 超時(shí)或者被篡改了,此時(shí)應(yīng)該強(qiáng)制跳轉(zhuǎn)到登錄界面。
axios.interceptors.response.use((response) => {
if (response.data.meta.status === 401) {
//返回401表示token失效,返回登陸界面
router.push('/login')
sessionStorage.clear()
window.location.reload()
}
return response
})
5.小結(jié)
- 前端權(quán)限的實(shí)現(xiàn)必須要后端提供數(shù)據(jù)支持,否則無(wú)法實(shí)現(xiàn)。
- 返回的權(quán)限數(shù)據(jù)的結(jié)構(gòu),前后端需要溝通協(xié)商怎樣的數(shù)據(jù)用起來(lái)才最方便。
5.1菜單控制
- 權(quán)限的數(shù)據(jù)需要在多組件之間共享,因此采用
vuex。 - 防止刷新界面權(quán)限數(shù)據(jù)丟失,所以需要使用
sessionStorage進(jìn)行持久化,并目要保證兩者的同步。
5.2界面控制
- 路由的導(dǎo)航守衛(wèi)可以防止跳過(guò)登錄界面。
- 動(dòng)態(tài)路由可以讓不具備權(quán)限的界面的路由規(guī)則壓根就不存在。
5.3按鈕控制
- 路由規(guī)則中可以增加路由元數(shù)據(jù)meta。
- 通過(guò)路由對(duì)象可以得到當(dāng)前的路由規(guī)則以及存在此規(guī)則中的meta數(shù)據(jù)。
- 自定義指令可以很方便的實(shí)現(xiàn)按鈕控制。
5.4請(qǐng)求和響應(yīng)控制
- 請(qǐng)求攔截器和響應(yīng)攔截器的使用。
- 請(qǐng)求方式的約定
restful。
總結(jié)
到此這篇關(guān)于前端權(quán)限控制和管理實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)前端權(quán)限控制和管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript中內(nèi)置對(duì)象Math的介紹及用法案例
Math對(duì)象是一個(gè)內(nèi)置對(duì)象,具有數(shù)學(xué)常數(shù)和函數(shù)的屬性和方法,不是一個(gè)函數(shù)對(duì)象,下面這篇文章主要給大家介紹了關(guān)于javascript中內(nèi)置對(duì)象Math的介紹及用法案例的相關(guān)資料,需要的朋友可以參考下2022-03-03
使用javascript做時(shí)間倒數(shù)讀秒功能的實(shí)例
今天小編就為大家分享一篇關(guān)于使用javascript做時(shí)間倒數(shù)讀秒功能的實(shí)例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01
uni-app小程序分享功能實(shí)現(xiàn)方法舉例
這篇文章主要給大家介紹了關(guān)于uni-app小程序分享功能實(shí)現(xiàn)方法的相關(guān)資料,uni-app中有分享的API接口,但是需要現(xiàn)在QQ或者微信等開(kāi)發(fā)者平臺(tái)上注冊(cè)賬號(hào),驗(yàn)證公司信息,而且只能分享圖片或者文本等內(nèi)容,需要的朋友可以參考下2023-07-07
使用JavaScript實(shí)現(xiàn)一個(gè)靜態(tài)鏈表
這篇文章主要為大家詳細(xì)介紹了如何使用JavaScript實(shí)現(xiàn)一個(gè)靜態(tài)鏈表,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以跟隨小編一起了解一下2023-06-06
javascript九宮格圖片隨機(jī)打亂位置的實(shí)現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了javascript九宮格圖片隨機(jī)打亂位置的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03

