前后端分離后權限的思路
最近看到許多關於權限的思路,但好像都是使用動態加載路由的方式,現在也分享下我在項目中使用的解決方案。
前后端分離關於權限的處理每個人都不一樣,根據項目選擇制定合適的方案就好
我的方案是:
- 前端掛載所有路由
- 通過 Api 接口獲取用戶權限標識(路由名稱)
- 在路由切換的時候進行權限校驗
- 至於頁面的權限按鈕則通過指令+自定義組件的形式封裝成通用權限按鈕,通過傳入相應的標識判斷是否現實按鈕
- 大體流程如下圖所示

前后端分離權限的實現
- 路由中定義是否需要驗證權限:
meta:{ auth :true }
路由定義
若是后端項目,一般會包含公用的頭部,側邊導航等公用的部分,這些部分可以將其抽象成一個個組件,創建布局頁使其更簡單的使用關於路由的引用,在大的項目中發現不適用懶加載能夠更快的熱更新,所以可以根據環境采取不同的加載方式一個簡單的路由
路由結構圖

_import.[env].js:
生產環境( production )/ testing 的定義
module.exports = file => () => import('@/views/' + file + '.vue')
開發環境( development )的定義
module.exports = file => require('@/views/' + file + '.vue').default
路由導入代碼
index.js: 使用 vue-router 掛載路由
import Vue from 'vue'
import Router from 'vue-router'
import { routes } from './routes'
import logic from './logic'
Vue.use(Router)
const router = new Router({
// mode: 'history',
routes
})
router.beforeEach(logic.beforeEach)
router.afterEach(logic.afterEach)
export default router
logic.js: 路由鈎子的實現
import { getInfo } from '@/api/modules/account'
const beforeEach = (to, from, next) => {
if (!to.meta.auth) {
return next()
}
if (!localStorage.token) {
return next('/login')
}
if (window.info) return next()
getInfo().then(res => {
localStorage.token = res.data.info.token
window.info = res.data.info
window.menus = res.data.menus
window.modules = res.data.modules
next()
})
}
const afterEach = (to, from) => {}
export default {
beforeEach,
afterEach
}
routes.js:路由的模塊定義
const _import = require('./_import_' + process.env.NODE_ENV)
export const appRouter = [
{
path: '/',
component: Layout,
children: [
{
path: '',
name: 'home',
component: _import('dashboard/index'),
meta: { defAuth: true, auth: true }
}
]
}
]
export const routes = [...appRouter]
接口定義
- account/login
登錄成功返回 token,保存到本地跳轉頁面 - auth/info
校驗是否有 token 信息及登錄信息,無則發送請求獲取登錄信息,菜單及權限模塊標識列表
菜單加載
新建 Layout 文件夾,將各部分組件再拆成小組件進行拼接后台組件,樣式使用聖杯布局,然后稍加改動就能夠實現基本的后台管理頁布局
Layout 結構圖

菜單結構:
"menus": [
{
"menuName": "控制台",
"menuIcon": null,
"menuCode": "home",
"menuUrl": null
},
{
"menuName": "系統管理",
"menuIcon": null,
"children": [
{
"menuName": "管理員管理",
"menuIcon": null,
"children": [
{
"menuName": "管理員列表",
"menuIcon": null,
"menuCode": "system-admin-list",
"menuUrl": null
}
]
},
{
"menuName": "角色列表",
"menuIcon": null,
"menuCode": "system-role-list",
"menuUrl": null
}
]
}
],
遞歸生成菜單組件
<template>
<dl>
<template v-for="(item,index) in menus">
<dt :key="item.menuName">
<a href="javascript:;" v-if="item.menuUrl" @click="$ui.redirect(item.menuUrl)">{{item.menuName}}</a>
<router-link :to="{name:item.menuCode}" v-else-if="item.menuCode">{{item.menuName}}</router-link>
<span v-else>{{item.menuName}}</span>
</dt>
<dd class="child-menu" :key="'c_'+index" v-if="item.children&&item.children.length>0">
<v-app-nav :menus="item.children" />
</dd>
</template>
</dl>
</template>
<script>
export default {
name: 'v-app-nav',
props: ['menus']
}
</script>
<style>
</style>
指令封裝
可以通過指令傳入需要的權限標識值,進行對按鈕權限的控制,根據需要控制顯示/啟用
定義
export default {
auth: {
inserted: (el, binding) => {
if (window.modules.indexOf(binding.value) === -1) {
// el.remove()
el.setAttribute('disabled', 'disabled')
}
}
}
}
使用
對於常用的一些按鈕,應封裝到組件中,統一管理風格,也能使起更易維護
<button v-auth="'role_create_create'">添加</button>
相關鏈接
文中的實現可以在下面倉庫中找到,不清楚的地方可以直接查看源碼
