前言
最近在開發管理系統時遇到了任何管理系統都會有的需求---權限控制,之前也遇到過這種需求,但是架構不完善導致的各種問題使得后期維護非常麻煩,這一次的方案解決了之前的種種問題,現做一次記錄,當然這個架構后期可能會有坑,不過得一步一步的嘗試才能發現并解決問題。
權限控制需求
因為是單頁面應用,路由交給前端來控制,對于一些需要特定權限才能查看的信息的保護變得尤為重要,如果前端不做好權限校驗,后端也一時疏忽,就可能就會導致數據泄露。
對于權限控制,需求大致為如下:
安全層面的思考
之前接手了一個管理系統,前端是將權限列表存儲在storage中來實現長久儲存,這種實現方式是很不可取的,因為hacker可以通過手動更改存儲的信息來實現獲取特定權限,甚至系統都沒有做路由攔截,如果知道模塊的路由,可以直接通過輸入路由信息來直接跳轉到特定模塊。對于一些模塊的權限,權限被管理員修改后也無法立即生效,所以對于這幾種情況做了如下思考與實踐。
權限被管理員修改后立即生效
對于這個需求,我的做法是,獲取到權限列表后,將權限信息存儲在 vuex store 中,并且使用getter函數,對于是否可以使用該權限進行判斷,這樣一旦權限數據更新,前端權限限制功能點會自動修改,從而做到權限的實時性,大致實現如下:
// vuex state.jsexport default { userPrivileges: { admin: [], purchaser: [] }, // 用戶權限信息}// vuex getters.jsexport default { canIUse: state => (role, id) => state.userPrivileges[role].includes(id)}// 頁面具體小功能,通過 mapGetters 引入 canIUse 函數<span v-if="canIUse('admin', 9)">{{scope.row.allocation_subtotal}}</span>這樣一來,數據存儲在內存中,那么權限信息就無法輕易的被修改,同時對于權限的判斷也非常簡單,只需要在特定功能點傳入功能點的權限id就能判斷是否可以使用這個權限了。
但是將數據存儲在了內存中也會遇到一個問題,頁面刷新怎么辦?接下來就是講解這種情況。
刷新頁面也可以進行權限判斷
對于大模塊的權限攔截,肯定是通過路由鉤子來進行攔截的(這種實現有很多文章講解過,這里不具體講解),但是通過路由鉤子進行攔截的前提是,權限信息得提前存在。
刷新頁面會存在這種情況,頁面刷新時,先執行的路由鉤子,再執行的組件生命周期鉤子來請求權限的列表,此時權限信息不存在,那么頁面跳轉到登陸頁的話,體驗就不夠友好。
所以我的做法是,建立一個中間頁,如果權限校驗不通過,那么跳轉至中間頁,中間頁進行權限的請求,請求到權限后,再判斷是否可以跳轉,這樣的話,刷新頁面體驗就比較好。大致代碼如下:
// 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') // 如果沒有任何權限,則跳轉登陸頁面 })}用戶在登陸后也可以跳轉到這個權限中間頁,進行權限判斷后再跳轉到對應模塊。
尾聲
大致的實現過程就是這樣,希望對大家有所幫助,如果有暗坑還請指出。也希望大家多多支持武林網。
新聞熱點
疑難解答