不同的權限對應不同的路由(菜單),同時側邊欄也根據權限異步生成,實現登錄和鑒權思路如下:
- 登錄:點擊登錄,服務器驗證通過后返回一個 token ,然后存到 cookie,再根據 token 拉取用戶權限
- 鑒權:通過 token 獲取對應的roles, 計算有權限的路由,使用 router.addRoutes 動態加載路由
數據和操作通過 vuex 進行控制
1 登錄
登錄按鈕 click 事件觸發登錄動作:
/** ...省略的代碼 */
this.$store.dispatch('LoginByUsername', {
'username': username, 'password': password
})
.then(() => {
this.logining = false
this.$notify.success({
message: '歡迎登錄',
duration: 3 * 1000,
})
// 重定向到首頁
this.$router.push({ path: this.redirect || '/' })
})
.catch(err => {
this.logining = false
this.$alert(err, {
type: 'warning',
confirmButtonText: 'ok'
})
})
/** ...省略的代碼 */
action:
// 登入
LoginByUsername({commit }, userInfo){
const username = userInfo.username.trim()
return new Promise((resolve, reject) => {
loginByUsernameApi(username, userInfo.password)
.then(response=> {
const data = response.data
setToken(data.token) // 儲存token
commit('SET_TOKEN', data.token)
commit('SET_ACCOUNT', username)
resolve()
})
.catch(err => {
reject(err)
})
})
}
/** ...省略的代碼 */
登錄成功,服務端返回一個token,然后儲存到本地 cookie。
2 獲取用戶權限
對每個路由,在全局鈎子 router.beforeEach 中攔截,判斷是否已獲取token,之后再獲取用戶的基本信息
/** ...省略的代碼 */
if(store.getters.token) {
// 判斷當前用戶是否已拉取完user_info信息
if(store.getters.roles.length === 0){
// 拉取用戶信息
store.dispatch('GetUserInfo').then(resp => {
const roles = resp.data.roles
next()
/** ...省略的代碼 */
})
})
}
}
/** ...省略的代碼 */
action:
// 獲取用戶信息, 名稱、頭像、權限
GetUserInfo({commit, state}) {
return new Promise((resolve, reject) => {
getLoginUserInfoApi(state.token)
.then(response => {
if(!response.data){
reject('error')
}
const data = response.data
const roles = data.data
if(roles && roles.length > 0){
commit('SET_ROLES', roles)
}else {
reject()
}
if(data.name){
commit('SET_NAME', data.name)
}
if(data.avatar){
commit('SET_AVATAR', data.avatar)
}
response.data.roles = roles
resolve(response)
})
.catch(err => {
reject(err)
})
})
},
3 菜單權限
前端保存一份路由表,記錄每一個路由和需要的權限。
再根據用戶信息里的 roles 計算對應的權限,然后生成有權限的菜單,再掛載路由。
但這只是頁面控制,后端也要相應的做權限驗證。
- 創建vue實例時使用vue-router掛載登錄和一些公用頁面,如首頁、圖表等
- 用戶登錄后,將獲取的roles和路由表的權限比較,生成用戶可訪問的路由表
- 調用router.addRoutes添加可訪問的路由
- 使用vuex管理路由表,生成側邊欄菜單
首先是 router.js 路由表
import Vue from 'vue'
import Router from 'vue-router'
import Container from '@/containers/Container'
import Login from '@/views/login'
import Page404 from '@/views/404'
import Dashboard from '@/views/dashboard'
/** router modules */
import systemRouter from './modules/system'
Vue.use(Router)
export const constantRouterMap = [
{
path: '/login',
hidden: true,
component: Login
},
{
path: '/404',
hidden: true,
component: Page404
},
{
path: '/',
redirect: '/dashboard',
component: Container,
name: '首頁',
hidden: false,
meta: { title: '首頁', icon: '', noCache: true },
children: [
{
path: 'dashboard',
name: 'Dashboard',
component: Dashboard,
meta: { title: '首頁', icon: 'fa fa-dashboard fa-lg', noCache: true }
},
{
path: 'table',
name: '表格綜合實例',
component: Form,
meta: { title: '表格綜合實例', icon: '', noCache: true }
},
// { path: '*', redirect: '/404', hidden: true }
]
},
]
export default new Router({
mode: 'hash',
scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap
})
export const asyncRouterMap = [
/** 其他的異步路由表 */
systemRouter,
{ path: '*', redirect: '/404', hidden: true }
]
同級目錄下的 ./modules/system.js 路由表
import Container from '@/containers/Container'
/**
* 系統管理相關路由
*/
const systemRouter = {
path: '/system',
component: Container,
redirect: '/system/permit/account',
name: '系統管理',
meta: {
title: '系統管理',
roles: ['/system']
},
children: [
{
path: 'permit',
name: '權限管理',
hidden: false,
redirect: '/system/permit/account',
component: () => import('@/views/system/permit'),
meta: {
title: '權限管理',
icon: 'fa fa-cog fa-lg',
roles: ['/system/permit']
},
children: [
{ path: 'account', name: '用戶',
component: () => import('@/views/system/permit/account'),
meta: { title: '用戶', icon: 'form', roles: ['/system/permit/account'] }
},
{ path: 'accountgroup', name: '用戶組',
component: () => import('@/views/system/permit/accountgroup'),
meta: { title: '用戶組', icon: 'form', roles: ['/system/permit/accountgroup'] }
},
{ path: 'role', name: '角色',
component: () => import('@/views/system/permit/role'),
meta: { title: '角色', icon: 'form', roles: ['/system/permit/role'] }
},
{ path: 'authorize', name: '授權',
component: () => import('@/views/system/permit/authorize'),
meta: { title: '授權', icon: 'form', roles: ['/system/permit/authorize'] }
},
]
},
],
}
export default systemRouter
roles: ['/system/permit/account'] 表示該頁面需要的權限是 '/system/permit/account' 后端返回的 roles 里有這個記錄則能訪問對應的頁面
而 '/system/permit/account' 是 '/system/permit' 的子路由,所以要訪問 '/system/permit/account',后端返回:
roles: ['/system', '/system/permit', '/system/permit/account']
注意: 404頁面要最后加載,如果放在 constantRouterMap 中,所有后面的頁面都會被攔截到404