最近在開發管理系統時遇到了任何管理系統都會有的需求---權限控制,以前也遇到過這種需求,可是架構不完善致使的各類問題使得後期維護很是麻煩,這一次的方案解決了以前的種種問題,現作一次記錄,固然這個架構後期可能會有坑,不過得一步一步的嘗試才能發現並解決問題。前端
由於是單頁面應用,路由交給前端來控制,對於一些須要特定權限才能查看的信息的保護變得尤其重要,若是前端不作好權限校驗,後端也一時疏忽,就可能就會致使數據泄露。vue
對於權限控制,需求大體爲以下:vuex
以前接手了一個管理系統,前端是將權限列表存儲在storage中來實現長久儲存,這種實現方式是很不可取的,由於hacker能夠經過手動更改存儲的信息來實現獲取特定權限,甚至系統都沒有作路由攔截,若是知道模塊的路由,能夠直接經過輸入路由信息來直接跳轉到特定模塊。對於一些模塊的權限,權限被管理員修改後也沒法當即生效,因此對於這幾種狀況作了以下思考與實踐。後端
對於這個需求,個人作法是,獲取到權限列表後,將權限信息存儲在 vuex store 中,而且使用getter函數,對因而否可使用該權限進行判斷,這樣一旦權限數據更新,前端權限限制功能點會自動修改,從而作到權限的實時性,大體實現以下:promise
// vuex state.js
export default {
userPrivileges: {
admin: [],
purchaser: []
}, // 用戶權限信息
}
// vuex getters.js
export default {
canIUse: state => (role, id) => state.userPrivileges[role].includes(id)
}
// 頁面具體小功能,經過 mapGetters 引入 canIUse 函數
<span v-if="canIUse('admin', 9)">{{scope.row.allocation_subtotal}}</span>
複製代碼
這樣一來,數據存儲在內存中,那麼權限信息就沒法輕易的被修改,同時對於權限的判斷也很是簡單,只須要在特定功能點傳入功能點的權限id就能判斷是否可使用這個權限了。安全
可是將數據存儲在了內存中也會遇到一個問題,頁面刷新怎麼辦?接下來就是講解這種狀況。架構
對於大模塊的權限攔截,確定是經過路由鉤子來進行攔截的(這種實現有不少文章講解過,這裏不具體講解),可是經過路由鉤子進行攔截的前提是,權限信息得提早存在。函數
刷新頁面會存在這種狀況,頁面刷新時,先執行的路由鉤子,再執行的組件生命週期鉤子來請求權限的列表,此時權限信息不存在,那麼頁面跳轉到登錄頁的話,體驗就不夠友好。fetch
因此個人作法是,創建一箇中間頁,若是權限校驗不經過,那麼跳轉至中間頁,中間頁進行權限的請求,請求到權限後,再判斷是否能夠跳轉,這樣的話,刷新頁面體驗就比較好。大體代碼以下:this
// vuex actions.js
// 經過返回一個promise,使得store更新與後續代碼變爲「同步」執行
export default {
getUserPrivileges({ commit }) {
return fetch.get({
url: '/currentstaff'
}).then(data => {
commit('SET_USER_PRIVILEGES_INFO', data.data)
return data.data
}).catch(e => {
})
}
}
// router.js
// 須要驗證權限的路由
{
path: 'suppliers',
component: Suppliers,
meta: {
role: 'admin',
privilegeId: 5
}
}
function isCanUseThisModule(to, from) {
return to.matched.every(record => {
// 利用路由meta存儲相應權限信息
if (record.meta.role) {
return store.getters.canIUse(record.meta.role, record.meta.privilegeId)
} else {
return true // 若是不須要權限,直接返回true
}
})
}
router.beforeEach((to, from, next) => {
if (isCanUseThisModule(to, from)) {
next() // 權限驗證經過,跳轉下一路由
} else {
next({
path: '/main/privilegeValidator' // 權限驗證不經過時的中間頁
})
}
})
// 權限校驗中間頁代碼示例
created() {
this.$store.dispatch('getUserPrivileges').then(data => {
for (let i = 0; i < data.admin_permissions.length; i++) {
if (this.canIUse('admin', data.admin_permissions[i])) {
this.$router.push({
path: this.routerList.find(value => value.privilegeId === data.admin_permissions[i]).linkHref
})
return
}
}
this.$router.push('/login') // 若是沒有任何權限,則跳轉登錄頁面
})
}
複製代碼
用戶在登錄後也能夠跳轉到這個權限中間頁,進行權限判斷後再跳轉到對應模塊。
大體的實現過程就是這樣,但願對你們有所幫助,若是有暗坑還請指出。