vue 權(quán)限管理幾種實(shí)現(xiàn)方法
權(quán)限管理
權(quán)限管理概念和方法
權(quán)限管理是指對(duì)系統(tǒng)、應(yīng)用程序或數(shù)據(jù)資源的訪問和使用進(jìn)行控制和管理的過程。
它確保只有經(jīng)過授權(quán)的用戶能夠獲得適當(dāng)?shù)臋?quán)限來(lái)執(zhí)行特定的操作或訪問敏感的信息。
權(quán)限管理在信息系統(tǒng)安全和數(shù)據(jù)保護(hù)方面起著重要的作用。
在權(quán)限管理中,以下是一些常見的概念和方法:
身份驗(yàn)證(Authentication):身份驗(yàn)證是確認(rèn)用戶身份的過程。通過用戶名和密碼、指紋、智能卡或其他認(rèn)證方式,系統(tǒng)可以驗(yàn)證用戶是否是其所聲稱的身份。
授權(quán)(Authorization):授權(quán)是系統(tǒng)根據(jù)用戶的身份和角色,確定用戶能夠執(zhí)行的操作和訪問的資源。授權(quán)通?;陬A(yù)定義的權(quán)限策略和規(guī)則來(lái)進(jìn)行。
角色管理(Role Management):角色是一組具有相似權(quán)限需求的用戶的集合。通過將用戶分配給角色,可以簡(jiǎn)化權(quán)限管理,并確保用戶只獲得其所需的最低權(quán)限。
權(quán)限繼承(Permission Inheritance):權(quán)限繼承是一種層級(jí)結(jié)構(gòu)的權(quán)限管理方法,其中較高級(jí)別的實(shí)體(如組織單位)擁有更高級(jí)別的權(quán)限,而較低級(jí)別的實(shí)體(如個(gè)人用戶)繼承這些權(quán)限。
最小權(quán)限原則(Principle of Least Privilege):最小權(quán)限原則指的是給予用戶執(zhí)行任務(wù)所需的最低權(quán)限,以減少潛在的安全風(fēng)險(xiǎn)。這意味著用戶只能訪問和操作他們工作所需的資源,而不會(huì)被授予超過必要的權(quán)限。
審計(jì)與監(jiān)控(Auditing and Monitoring):審計(jì)功能可以跟蹤和記錄用戶對(duì)系統(tǒng)和資源的訪問日志。監(jiān)控功能可實(shí)時(shí)監(jiān)控用戶的行為,并及時(shí)檢測(cè)異常活動(dòng)或未經(jīng)授權(quán)的訪問。
定期審查(Regular Review):定期審查權(quán)限設(shè)置和用戶權(quán)限是一項(xiàng)重要的管理措施,以確保權(quán)限與用戶的實(shí)際需求保持一致,并防止權(quán)限的濫用和泄露。
權(quán)限管理是確保系統(tǒng)和數(shù)據(jù)資源安全和保護(hù)的關(guān)鍵措施,通過身份驗(yàn)證、授權(quán)、角色管理等方法來(lái)管理用戶的權(quán)限,以及通過審計(jì)和監(jiān)控來(lái)追蹤和防止未經(jīng)授權(quán)的訪問。
權(quán)限管理是什么
權(quán)限是對(duì)特定資源的訪問許可,所謂權(quán)限控制,也就是確保用戶只能訪問到被分配的資源
而前端權(quán)限歸根結(jié)底是 請(qǐng)求的發(fā)起權(quán),請(qǐng)求的發(fā)起可能有下面兩種形式觸發(fā)
- 頁(yè)面加載觸發(fā)
- 頁(yè)面上的按鈕點(diǎn)擊觸發(fā)
所有的請(qǐng)求發(fā)起都觸發(fā)自前端路由或視圖
所以我們可以從這兩方面入手,對(duì)觸發(fā)權(quán)限的源頭進(jìn)行控制,最終要實(shí)現(xiàn)的目標(biāo)是:
- 路由方面,用戶登錄后只能看到自己有權(quán)訪問的導(dǎo)航菜單,也只能訪問自己有權(quán)訪問的路由地址.否則將跳轉(zhuǎn) 4xx 提示頁(yè)
- 視圖方面,用戶只能看到自己有權(quán)瀏覽的內(nèi)容和有權(quán)操作的控件
- 最后再加上 請(qǐng)求控制 作為最后一道防線,路由可能配置失誤,按鈕可能忘了加權(quán)限,這種時(shí)候請(qǐng)求控制可以用來(lái)兜底,越權(quán)請(qǐng)求將在前端被攔截
前端如何做權(quán)限控制
前端權(quán)限控制可以分為四個(gè)方面
- 接口權(quán)限
- 按鈕權(quán)限
- 菜單權(quán)限
- 路由權(quán)限
接口權(quán)限
接口權(quán)限目前一般采用 jwt 的形式來(lái)驗(yàn)證,沒有通過的話一般返回 401 ,跳轉(zhuǎn)到登錄頁(yè)面重新進(jìn)行登錄
登錄完拿到 token ,將 token 存起來(lái),通過 axios 請(qǐng)求攔截器進(jìn)行攔截,每次請(qǐng)求的時(shí)候頭部攜帶 token
axios.interceptors.request.use(config => {
config.headers['token'] = cookie.get('token')
return config
})
axios.interceptors.response.use(res=>{},{response}=>{
if (response.data.code === 40099 || response.data.code === 40098) { //token過期或者錯(cuò)誤
router.push('/login')
}
})
路由權(quán)限
方案一
初始化即掛載全部路由,并且在路由上標(biāo)記相應(yīng)的權(quán)限信息,每次路由跳轉(zhuǎn)前做校驗(yàn)
const routerMap = [
{
path: '/permission',
component: Layout,
redirect: '/permission/index',
alwaysShow: true, // will always show the root menu
meta: {
title: 'permission',
icon: 'lock',
roles: ['admin', 'editor'] // you can set roles in root nav
},
children: [
{
path: 'page',
component: () => import('@/views/permission/page'),
name: 'pagePermission',
meta: {
title: 'pagePermission',
roles: ['admin'] // or you can only set roles in sub nav
}
},
{
path: 'directive',
component: () => import('@/views/permission/directive'),
name: 'directivePermission',
meta: {
title: 'directivePermission'
// if do not set roles, means: this page does not require permission
}
}]
}
]
這種方式存在以下四種缺點(diǎn):
- 加載所有的路由,如果路由很多,而用戶并不是所有的路由都有權(quán)限訪問,對(duì)性能會(huì)有影響。
- 全局路由守衛(wèi)里,每次路由跳轉(zhuǎn)都要做權(quán)限判斷。
- 菜單信息寫死在前端,要改個(gè)顯示文字或權(quán)限信息,需要重新編譯
- 菜單跟路由耦合在一起,定義路由的時(shí)候還有添加菜單顯示標(biāo)題,圖標(biāo)之類的信息,而且路由不一定作為菜單顯示,還要多加字段進(jìn)行標(biāo)識(shí)
方案二
初始化的時(shí)候先掛載不需要權(quán)限控制的路由,比如登錄頁(yè),404等錯(cuò)誤頁(yè)。
如果用戶通過URL進(jìn)行強(qiáng)制訪問,則會(huì)直接進(jìn)入404,相當(dāng)于從源頭上做了控制
登錄后,獲取用戶的權(quán)限信息,然后篩選有權(quán)限訪問的路由,在全局路由守衛(wèi)里進(jìn)行調(diào)用 addRoutes 添加路由
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css'// progress bar style
import { getToken } from '@/utils/auth' // getToken from cookie
NProgress.configure({ showSpinner: false })// NProgress Configuration
// permission judge function
function hasPermission(roles, permissionRoles) {
if (roles.indexOf('admin') >= 0) return true // admin permission passed directly
if (!permissionRoles) return true
return roles.some(role => permissionRoles.indexOf(role) >= 0)
}
const whiteList = ['/login', '/authredirect']// no redirect whitelist
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
if (getToken()) { // determine if there has token
/* has token*/
if (to.path === '/login') {
next({ path: '/' })
NProgress.done() // if current page is dashboard will not trigger af terEach hook, so manually handle it
} else {
if (store.getters.roles.length === 0) { // 判斷當(dāng)前用戶是否已拉取完user_info信息
store.dispatch('GetUserInfo').then(res => { // 拉取user_info信息
const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop']
store.dispatch('GenerateRoutes', { roles }).then(() => { //根據(jù) roles權(quán)限生成可訪問的路由表
router.addRoutes(store.getters.addRouters) // 動(dòng)態(tài)添加可訪問路由表
next({ ...to, replace: true }) // hack方法 確保addRoutes已完成,set the replace: true so the navigation will not leave a history record
})
}).catch((err) => {
store.dispatch('FedLogOut').then(() => {
Message.error(err || 'Verification failed, please login again')
next({ path: '/' })
})
})
} else {
// 沒有動(dòng)態(tài)改變權(quán)限的需求可直接next() 刪除下方權(quán)限判斷 ↓
if (hasPermission(store.getters.roles, to.meta.roles)) {
next()//
} else {
next({ path: '/401', replace: true, query: { noGoBack: true }})
}
// 可刪 ↑
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) { // 在免登錄白名單,直接進(jìn)入
next()
} else {
next('/login') // 否則全部重定向到登錄頁(yè)
NProgress.done() // if current page is login will not trigger afterE ach hook, so manually handle it
}
}
})
router.afterEach(() => {
NProgress.done() // finish progress bar
})
按需掛載,路由就需要知道用戶的路由權(quán)限,也就是在用戶登錄進(jìn)來(lái)的時(shí)候就要知道當(dāng)前用戶擁有哪些路由權(quán)限
這種方式也存在了以下的缺點(diǎn):
- 全局路由守衛(wèi)里,每次路由跳轉(zhuǎn)都要做判斷
- 菜單信息寫死在前端,要改個(gè)顯示文字或權(quán)限信息,需要重新編譯
- 菜單跟路由耦合在一起,定義路由的時(shí)候還有添加菜單顯示標(biāo)題,圖標(biāo)之類的信息,而且路由不一定作為菜單顯示,還要多加字段進(jìn)行標(biāo)識(shí)
菜單權(quán)限
菜單權(quán)限可以理解成將頁(yè)面與理由進(jìn)行解耦
方案一
菜單與路由分離,菜單由后端返回, 前端定義路由信息
{
name: "login",
path: "/login",
component: () => import("@/pages/Login.vue")
}
name 字段都不為空,需要根據(jù)此字段與后端返回菜單做關(guān)聯(lián),后端返回的菜單信息中必須要有 name 對(duì)應(yīng)的字段,并且做唯一性校驗(yàn)
全局路由守衛(wèi)里做判斷
function hasPermission(router, accessMenu) {
if (whiteList.indexOf(router.path) !== -1) {
return true;
}
let menu = Util.getMenuByName(router.name, accessMenu);
if (menu.name) {
return true;
}
return false;
}
Router.beforeEach(async (to, from, next) => {
if (getToken()) {
let userInfo = store.state.user.userInfo;
if (!userInfo.name) {
try {
await store.dispatch("GetUserInfo")
await store.dispatch('updateAccessMenu')
if (to.path === '/login') {
next({ name: 'home_index' })
} else {
//Util.toDefaultPage([...routers], to.name, router, next);
next({ ...to, replace: true }) // 菜單權(quán)限更新完成,重新進(jìn)一次當(dāng)前路由
}
}
catch (e) {
if (whiteList.indexOf(to.path) !== -1) { // 在免登錄白名單,直接進(jìn)入
next()
} else {
next('/login')
}
}
} else {
if (to.path === '/login') {
next({ name: 'home_index' })
} else {
if (hasPermission(to, store.getters.accessMenu)) {
Util.toDefaultPage(store.getters.accessMenu,to, routes, next);
} else {
next({ path: '/403',replace:true })
}
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) { // 在免登錄白名單,直接進(jìn)入
next()
} else {
next('/login')
}
}
let menu = Util.getMenuByName(to.name, store.getters.accessMenu);
Util.title(menu.title);
});
Router.afterEach((to) => {
window.scrollTo(0, 0);
});
方案二
菜單和路由都由后端返回
前端統(tǒng)一定義路由組件
const Home = () => import("../pages/Home.vue");
const UserInfo = () => import("../pages/UserInfo.vue");
export default {
home: Home,
userInfo: UserInfo
};
后端路由組件返回以下格式
[
{
name: "home",
path: "/",
component: "home"
},
{
name: "home",
path: "/userinfo",
component: "userInfo"
}
]
在將后端返回路由通過 addRoutes 動(dòng)態(tài)掛載之間,需要將數(shù)據(jù)處理一下,將 cmponent 字段換為真正的組件
如果有嵌套路由,后端功能設(shè)計(jì)的時(shí)候,要注意添加相應(yīng)的字段,前端拿到數(shù)據(jù)也要做相應(yīng)的處理
這種方法也會(huì)存在缺點(diǎn):
- 全局路由守衛(wèi)里,每次路由跳轉(zhuǎn)都要做判斷
- 前后端的配合要求更高
按鈕權(quán)限
方案一
按鈕權(quán)限也可以用 v-if 判斷
但是如果頁(yè)面過多,每個(gè)頁(yè)面頁(yè)面都要獲取用戶權(quán)限 role 和路由表里的 meta.btnPermissions ,然后再做判斷
具體情況如下:
在Vue中,你可以使用路由表中的meta.btnPermissions來(lái)進(jìn)行按鈕權(quán)限控制。meta.btnPermissions是一個(gè)對(duì)象,其中包含用于確定按鈕是否可見或可用的權(quán)限信息。
下面是一個(gè)示例,展示了如何使用路由表中的meta.btnPermissions來(lái)控制按鈕的權(quán)限:
- 首先,確保你已經(jīng)安裝并配置了Vue Router。
- 在你的路由表中,為每個(gè)路由添加
meta.btnPermissions字段。例如:
const routes = [
{
path: '/dashboard',
component: Dashboard,
meta: {
btnPermissions: {
view: true,
edit: false,
delete: false,
},
},
},
{
path: '/users',
component: Users,
meta: {
btnPermissions: {
view: true,
edit: true,
delete: true,
},
},
},
// 其他路由...
];
在上述示例中,每個(gè)路由都有一個(gè)meta.btnPermissions字段,其中包含了該路由相關(guān)的按鈕權(quán)限信息。
你可以根據(jù)實(shí)際需求設(shè)置每個(gè)按鈕的權(quán)限。
- 在你的組件中,使用
this.$router來(lái)訪問路由表,并根據(jù)當(dāng)前路由的meta.btnPermissions來(lái)控制按鈕的顯示和可用性。例如:
<template>
<div>
<button v-if="checkButtonPermissions('view')">查看</button>
<button v-if="checkButtonPermissions('edit')">編輯</button>
<button v-if="checkButtonPermissions('delete')">刪除</button>
</div>
</template>
<script>
export default {
methods: {
checkButtonPermissions(permission) {
const route = this.$router.currentRoute; // 獲取當(dāng)前路由對(duì)象
const meta = route.meta.btnPermissions; // 獲取當(dāng)前路由的meta.btnPermissions對(duì)象
return meta[permission]; // 返回指定權(quán)限的值
},
},
};
</script>
我們通過this.$router.currentRoute獲取當(dāng)前路由對(duì)象,然后從路由對(duì)象的meta.btnPermissions字段中獲取按鈕權(quán)限信息。
最后,通過checkButtonPermissions方法判斷按鈕是否可見或可用。你可以根據(jù)實(shí)際需求調(diào)整該方法的實(shí)現(xiàn)。
方案二
通過自定義指令進(jìn)行按鈕權(quán)限的判斷
首先配置路由
{
path: '/permission',
component: Layout,
name: '權(quán)限測(cè)試',
meta: {
btnPermissions: ['admin', 'supper', 'normal'] // 頁(yè)面需要的權(quán)限
},
children: [
{
path: 'supper',
component: _import('system/supper'),
name: '權(quán)限測(cè)試',
meta: {
btnPermissions: ['admin', 'supper']
} // 頁(yè)面需要的權(quán)限
},
{
path: 'normal',
component: _import('system/normal'),
name: '權(quán)限測(cè)試',
meta: {
btnPermissions: ['admin']
} // 頁(yè)面需要的權(quán)限
}
]
}
自定義權(quán)限鑒定指令
import Vue from 'vue'
/**權(quán)限指令**/
const has = Vue.directive('has', {
bind: function (el, binding, vnode) {
// 獲取頁(yè)面按鈕權(quán)限
let btnPermissionsArr = [];
if(binding.value){
// 如果指令傳值,獲取指令參數(shù),根據(jù)指令參數(shù)和當(dāng)前登錄人按鈕權(quán)限做比較。
btnPermissionsArr = Array.of(binding.value);
}else{
// 否則獲取路由中的參數(shù),根據(jù)路由的btnPermissionsArr和當(dāng)前登錄人按鈕權(quán)限做比較。
btnPermissionsArr = vnode.context.$route.meta.btnPermissions;
}
if (!Vue.prototype.$_has(btnPermissionsArr)) {
el.parentNode.removeChild(el);
}
}
});
// 權(quán)限檢查方法
Vue.prototype.$_has = function (value) {
let isExist = false;
// 獲取用戶按鈕權(quán)限
let btnPermissionsStr = sessionStorage.getItem("btnPermissions");
if (btnPermissionsStr == undefined || btnPermissionsStr == null) {
return false;
}
if (value.indexOf(btnPermissionsStr) > -1) {
isExist = true;
}
return isExist;
};
export {has}
在使用的按鈕中只需要引用 v-has 指令
<el-button @click='editClick' type="primary" v-has>編輯</el-button>
小結(jié)
關(guān)于權(quán)限如何選擇哪種合適的方案,可以根據(jù)自己項(xiàng)目的方案項(xiàng)目,如考慮路由與菜單是否分離
權(quán)限需要前后端結(jié)合,前端盡可能的去控制,更多的需要后臺(tái)判斷
到此這篇關(guān)于vue 權(quán)限管理幾種實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)vue 權(quán)限管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Vue中實(shí)現(xiàn)權(quán)限管理詳解
- vue登錄路由權(quán)限管理的項(xiàng)目實(shí)踐
- vue實(shí)現(xiàn)前端按鈕組件權(quán)限管理
- Vue?element實(shí)現(xiàn)權(quán)限管理業(yè)務(wù)流程詳解
- 深入解析vue中的權(quán)限管理
- vue?router權(quán)限管理實(shí)現(xiàn)不同角色顯示不同路由
- 淺談vue權(quán)限管理實(shí)現(xiàn)及流程
- Vue 指令實(shí)現(xiàn)按鈕級(jí)別權(quán)限管理功能
- vue權(quán)限管理系統(tǒng)的實(shí)現(xiàn)代碼
- 關(guān)于Vue的路由權(quán)限管理的示例代碼
相關(guān)文章
vue3使用SSE實(shí)現(xiàn)前端全局事件通訊方式
文章介紹了使用Vue3和VueUse實(shí)現(xiàn)基于SSE(Server-Sent Events)的全局事件通訊系統(tǒng),SSE具有單向通信、基于HTTP、自動(dòng)重連和輕量級(jí)等特點(diǎn),適合實(shí)時(shí)性要求高的應(yīng)用,實(shí)現(xiàn)方案包括訂閱消息、取消訂閱和關(guān)閉連接等功能2025-10-10
詳解vue.js 開發(fā)環(huán)境搭建最簡(jiǎn)單攻略
本篇文章主要介紹了vue.js 開發(fā)環(huán)境搭建最簡(jiǎn)單攻略,這里整理了詳細(xì)的步驟,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
使用Vue+Element UI實(shí)現(xiàn)復(fù)選框刪除線效果
在開發(fā)報(bào)告生成、配置選擇等功能時(shí),我們經(jīng)常需要讓用戶選擇包含哪些內(nèi)容,一個(gè)好的UI設(shè)計(jì)應(yīng)該讓用戶清晰地看到哪些選項(xiàng)被選中,哪些被排除,本文將介紹如何使用Vue + Element UI實(shí)現(xiàn)一個(gè)優(yōu)雅的復(fù)選框刪除線效果,需要的朋友可以參考下2026-01-01
在導(dǎo)入.vue文件的時(shí)候,ts報(bào)錯(cuò)提示:找不到模塊“@/Layout/index.vue”或其相應(yīng)的類型聲明問題
這篇文章主要介紹了在導(dǎo)入.vue文件的時(shí)候,ts報(bào)錯(cuò)提示:找不到模塊“@/Layout/index.vue”或其相應(yīng)的類型聲明問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
基于Vue實(shí)現(xiàn)HTML轉(zhuǎn)PDF并導(dǎo)出
這篇文章主要為大家介紹了三種方法,可以實(shí)現(xiàn)將HTML頁(yè)面轉(zhuǎn)為PDF并實(shí)現(xiàn)下載。文中的示例代碼講解詳細(xì),感興趣的小伙伴可以學(xué)習(xí)一下2022-04-04
vue js秒轉(zhuǎn)天數(shù)小時(shí)分鐘秒的實(shí)例代碼
這篇文章主要介紹了vue js秒轉(zhuǎn)天數(shù)小時(shí)分鐘秒的實(shí)例代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-08-08

