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


這樣當用戶A在地址欄輸入自己不能訪問的路由時,就不會跳轉到該頁面,而是跳轉到404頁面。

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

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

添加自定義指令 控制按鈕:
import Vue from 'vue'
import store from '../store';
import router from '../router';
const permission = {
inserted(el, binding) {
//傳過來的參數(shù)中,action表示被綁定的按鈕是什么操作;effect表示當用戶沒有這個操作權限的時候,
//應該如何如理這個按鈕
const action = binding.value.action
const effect = binding.value.effect
//獲取用戶在當前路由中所具有的權限列表
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)請求控制
除了登錄請求都得要帶上token,這樣服務器才可以鑒別你的身份。這塊需要配置axios的請求攔截器。

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

