前端權(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è)用戶是否能夠查詢數(shù)據(jù),是否能夠修改數(shù)據(jù)等操作。
ps:后端如何知道該請(qǐng)求是哪個(gè)用戶發(fā)過(guò)來(lái)的
- cookie
- session
- token
ps:后端的權(quán)限設(shè)計(jì)RBAC(一般五張表)
- 用戶
- 角色
- 權(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ù)器的壓力;
- 提高用戶體驗(yàn);
- 根據(jù)用戶具備的權(quán)限為該用戶展現(xiàn)自己權(quán)限范圍內(nèi)的內(nèi)容,避免在界面上給用戶帶來(lái)困擾,讓用戶專注于分內(nèi)之事;
3.前端權(quán)限控制思路
3.1菜單的權(quán)限控制
在登錄請(qǐng)求中,會(huì)得到用戶的權(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)限控制
如果用戶沒(méi)有登錄,手動(dòng)在地址欄敲入登錄后主界面的地址,則需要跳轉(zhuǎn)到登錄界面。
如果用戶已經(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)限控制
如果用戶通過(guò)非常規(guī)操作,比如通過(guò)瀏覽器調(diào)試工具將某些禁用的按鈕變成啟用狀態(tài),此時(shí)發(fā)的請(qǐng)求也應(yīng)該被前端所攔截。
4.實(shí)現(xiàn)步驟
4.1菜單欄控制
用戶登錄之后,服務(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ù)保持同步(用專門(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)題:這樣用戶在登錄之后就可以訪問(wèn)其他界面了,但如果用戶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ì)于用戶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)前用戶所擁有的的權(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)用戶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按鈕的控制
雖然用戶可以看到某些界面了,但是對(duì)于這個(gè)界面的一些按鈕,該用戶可能是沒(méi)有權(quán)限的。 因此,我們需要對(duì)組件中的一些按鈕進(jìn)行控制,用戶不具備權(quán)限的按鈕就隱藏或者禁用,而在這塊的實(shí)現(xiàn)中,可以把該邏輯放到自定義指令中
比如我們可以根據(jù)后端返回的數(shù)據(jù)right來(lái)判斷用戶有什么權(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)用戶沒(méi)有這個(gè)操作權(quán)限的時(shí)候, //應(yīng)該如何如理這個(gè)按鈕 const action = binding.value.action const effect = binding.value.effect //獲取用戶在當(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用戶是不能夠操作該頁(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] //獲取用戶在當(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遍歷對(duì)象和數(shù)組的方法總結(jié)
這篇文章介紹了javaScript遍歷對(duì)象和數(shù)組的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06詳解webpack 入門(mén)總結(jié)和實(shí)踐(按需異步加載,css單獨(dú)打包,生成多個(gè)入口文件)
本篇文章主要介紹了webpack 入門(mén)總結(jié)和實(shí)踐(按需異步加載,css單獨(dú)打包,生成多個(gè)入口文件) ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06gulp-htmlmin壓縮html的gulp插件實(shí)例代碼
這篇文章主要介紹了gulp-htmlmin壓縮html的gulp插件實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-06-06js+html獲取系統(tǒng)當(dāng)前時(shí)間
這篇文章主要為大家詳細(xì)介紹了javascript html獲取系統(tǒng)當(dāng)前時(shí)間,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-119個(gè)JavaScript評(píng)級(jí)/投票插件
在訪問(wèn)某個(gè)網(wǎng)站或者博客時(shí),如果該站點(diǎn)為用戶提供內(nèi)容的評(píng)級(jí)或投票功能的話,可以增強(qiáng)用戶參與的交互性之外,更可以給用戶一種“主人”的親切感,使得用戶可以切實(shí)地參與到網(wǎng)站內(nèi)容的評(píng)價(jià)體系中來(lái)。2010-01-01小程序開(kāi)發(fā)基礎(chǔ)之view視圖容器
這篇文章主要介紹了小程序開(kāi)發(fā)基礎(chǔ)之view視圖容器,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08JavaScript中如何跳出forEach循環(huán)代碼示例
循環(huán)遍歷一個(gè)元素是開(kāi)發(fā)中最常見(jiàn)的需求之一,下面這篇文章主要給大家介紹了關(guān)于JavaScript中如何跳出forEach循環(huán)的相關(guān)資料,文章通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-06-06