記得當年面試的時候,面試官問我,前端怎麼作權限控制,咱也不太會這個,只能尷尬回答道:「都是老大搭的架子,我只負責寫業務模塊代碼」,😭😭😭。
現在本身也作了不少項目了,以爲有必有對前端權限控制作一個總結。前端
前端權限控制一直是前端必須掌握的一個知識點,通常來講稍微正規一點的後臺系統確定有權限控制。固然仍是那句老話,前端原本就是不安全的,真正的安全仍是須要後端兄弟去把關,因此後端也必須按作權限控制!
咱們前端的權限校驗主要的目的是過濾不應有的請求和操做,減小服務端壓力。vue
我我的認爲前端權限控制應該分爲四個方面,接口權限、按鈕權限,頁面權限,路由權限,下面就分四個部分探討下權限控制怎麼作node
接口權限最簡單,目前通常採用jwt的形式來驗證,沒有經過的話通常返回401 Authentication Required, 返回登陸頁從新登陸
登陸完拿到Token
,將token存起來(cookie或者ssessionStorage),每次登陸的時候頭部攜帶token就好了(axios請求攔截器實現)。ios
const {token} = login() cookie.set('token',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過時或者錯誤 router.push('/login') } })
一個頁面會有新增,刪除,編輯等等按鈕。不一樣用戶應該是有不一樣操做權限的。
咱們不妨定義權限碼 0:不可見 1:不可編輯 2:可編輯
咱們提早和後端約定好按鈕的名字,後端會返回一個按鈕權限列表。而後咱們根據權限列表使用v-if指令或者 綁定disabled屬性達到相應權限效果。
固然更好的最好是本身寫一個自定義權限指令,實質就是根據相應權限操做dom面試
好比概覽頁面的編輯按鈕 名字先和後端定義好叫作overview-editvuex
// overviwe.vue overview是概覽頁面的路由名 ... <button v-auth='edit'> ... //util.js 全局註冊自定義指令 Vue.directive('auth', { inserted: function (el, binding, vnode) { const optName = binding.arg const authName = `${routeName}-${optName}` //這裏根據路由名和操做類型拼出按鈕名 overview-edit const btnAuthList = store.state.auth.btnAuthList if (btnAuthList[authName]===0) { // 按鈕權限爲0則移除dom el.parentNode.removeChild(el) } else if (btnAuthList[authName]===1) { // 按鈕權限爲1則禁用按鈕 vnode.componentInstance.disabled = true } } }) // 登陸的時候接受按鈕權限並存在vuex裏面 const {btnAuthList} = login() vuex.state.btnAuthList = btnAuthList
我的認爲頁面權限實際上就是菜單權限,若是說咱們沒有去某個頁面的導航菜單,實際上就是沒有去那個頁面的權限了,因此說頁面權限的實際就是菜單權限。數據庫
一句話,獲取菜單權限列表,動態遞歸生成菜單
這個菜單權限列表能夠是後臺直接返回你的,也能夠是你註冊路由的時候寫在meta裏面的菜單信息,後臺返回路由權限,你根據meta信息動態算出的菜單權限。
至於菜單確定是根據菜單權限遞歸生成的element-ui
//若是是定義在route信息裏面會是這種樣子 //咱們能夠根據後端返回的路由權限結合meta算出菜單權限 { name:xxx path:xxx meta: { role: [xxx,xxx,xxx] //哪些角色有資格 MenuIcon: 'xxxx' //菜單圖標 MenuTitle: 'xxx' //菜單名 } } //固然也能夠麻煩後臺直接生成菜單權限返回來 const {menuList} = login() //存vuex裏 vuex.state.menuList = menuList //在側邊欄或者頂部菜單組件裏動態生成菜單 //這裏基本都是用的UI庫,好比element-ui的NavMenu來實現的,你們有興趣能夠本身看文檔,固然也能夠本身遞歸實現,不難 <navMenu/ :menuList=menuList>
上面的菜單權限雖然作到能看不見菜單,可是我能夠經過直接輸入url的方式去沒有權限的頁面呀,這種狀況須要靠咱們的路由權限
來阻止。axios
這裏有兩個方案
第一種,也是我目前項目用的,先註冊好全部的路由,而後獲取有資格訪問的路由權限列表,最後直接經過Router.beforeEach來判斷,每次跳路由的時候判斷是否在權限列表裏,在的話就放行,不在就提示權限不夠
優勢:簡單暴力,不會跳到404頁面(由於去的路由能在路由規則裏找到)
缺點:因爲初始化了全部路由,運行的時候會掛載沒必要要的路由(?有待考究)
第二種,先只註冊基本路由,而後獲取路由權限列表,而後藉助route.add() API根據權限列表將有權限的路由動態註冊到路由規則上
優缺點與第一種正好相反segmentfault
這裏只寫第一種方案,第二種你們自行google
const {routeAuthList} = login() cosnt whiteList = ['/login','/','/404'] //合併生成總路由 whiteList = whiteList.contact(routeAuthList) //存vuex vuex.state.whiteList = whiteList //路由守衛判斷 router.beforeEach((to, from, next) => { //權限校驗 let pass = whiteList.inclue(to); if(!pass){ return console.log('無權訪問'); } next(); });
這個我以爲實際沒有定論,相關的文章也讀了許多,什麼作法都有,仍是須要結合實際業務。
好比
1.按鈕權限特別少的,那麼後端不用返回按鈕權限,直接前端生成鈕權限就行。甚至不用使用自定義指定,直接v-if,disable就行
2.權限頁面特別多,次級路由也特別多,那麼也能夠前端這邊生成路由權限,由於若是後端返的話,每次定義一個次級頁面都得讓後端在數據庫加一條數據,太麻煩人了,不方便
3.後端也不是說非得返回權限路由列表的,像我目前的項目就是返回的菜單列表,而後我根據菜單列表名手動算出權限列表。其實都同樣,返回權限列表也是根據權限列表裏面的meta算出菜單列表,有毛區別?
關於緩存,你們能夠看我這篇文章緩存
獲取的我的信息(包括權限列表)該不應緩存到ssessionStorage裏面?我看不少人的文章都是隻緩存token,每次刷新都是從新拉取信息。
我的認爲這樣作意義不大,緩存的目的就是爲了減小請求,優化交互。存在當前頁籤基本能保證同一時間你就是你。再說防君子不防小人,至於真的是小人,篡改ssessionStorage數據,咱不是還有後臺兄弟的校驗嗎,怕個卵。
總結了前端全部的權限控制,並給出了相應的僞碼實現,但願能給你們帶來應該的幫助