前言
在管理系統(tǒng)中最讓人頭疼的就是權(quán)限管理了,今天和大家一起討論下在Vue技術(shù)棧中怎么實現(xiàn)簡單實用用戶的權(quán)限控制,權(quán)限控制一般分為路由金闽、視圖暇唾、接口等方面促脉。首先學(xué)習(xí)一下vuerouter的的基礎(chǔ)知識及如何配置路由
路由基礎(chǔ)知識
<router-link :to="'home'">Home</router-link>
<router-link :to="{ path: 'home' }">Home</router-link>
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
等同于router.push()
當(dāng)然除了to是必須配置的屬性外辰斋,router-link還有一系列的屬性讓開發(fā)能夠更加靈活的控制路由切換及樣式上的優(yōu)化,router-link會被渲染成<a href="home"></a>當(dāng)然通過配置也可以修改渲染成其他html元素具體可以參考官方文檔
<keep-alive>
<router-view></router-view>
</keep-alive>
<router-view> 組件是一個 functional 組件瘸味,渲染路徑匹配到的視圖組件宫仗。<router-view> 渲染的組件還可以內(nèi)嵌自己的 <router-view>,根據(jù)嵌套路徑旁仿,渲染嵌套組件藕夫。
Router 實例方法
router.beforeEach((to, from, next) => {
/* must call `next` */
})
router.beforeResolve((to, from, next) => {
/* must call `next` */
})
router.afterEach((to, from) => {})
router.push()
router.replace()
router.go()
router.back()
router.forward()
router.addRoutes
VueRouter新版本添加了addRouter的方法,通過此方法可以在登錄成功后搭配Vuex的動態(tài)生成符合用戶權(quán)限的路由
路由表配置
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
import Layout from '../views/layout/Layout'
export const constantRouterMap = [
{ path: '/login', component: () => import('@/views/login/index'), hidden: true },
{ path: '/404', component: () => import('@/views/404'), hidden: true },
{
path: '/',
component: Layout,
redirect: '/dashboard',
name: 'Dashboard',
hidden: true,
children: [{
path: 'dashboard',
component: () => import('@/views/dashboard/index')
}]
}
]
export default new Router({
// mode: 'history', //后端支持可開
scrollBehavior: () => ({ y: 0 }),
routes: constantRouterMap
})
export const asyncRouterMap = [
{
path: '/example',
component: Layout,
redirect: '/example/table',
name: 'Example',
meta: { title: '案例', roles: ['admin', 'editor', 'guest'] },
children: [
{
path: 'table',
name: 'Table',
component: () => import('@/views/table/index'),
meta: { title: '表格', roles: ['admin', 'editor'] }
},
{
path: 'tree',
name: 'Tree',
component: () => import('@/views/tree/index'),
meta: { title: '樹形菜單', roles: ['admin', 'editor'] }
}
]
},
{
path: '/form',
component: Layout,
children: [
{
path: 'index',
name: 'Form',
component: () => import('@/views/form/index'),
meta: { title: '表單', roles: ['admin'] }
}
]
},
{
path: '/nested',
component: Layout,
redirect: '/nested/menu1',
name: 'Nested',
meta: {
title: '樹形菜單',
roles: ['admin']
},
children: [
{
path: 'menu1',
component: () => import('@/views/nested/menu1/index'), // Parent router-view
name: 'Menu1',
meta: { title: '菜單1' },
children: [
{
path: 'menu1-1',
component: () => import('@/views/nested/menu1/menu1-1'),
name: 'Menu1-1',
meta: { title: '菜單1-1' }
},
{
path: 'menu1-2',
component: () => import('@/views/nested/menu1/menu1-2'),
name: 'Menu1-2',
meta: { title: '菜單1-2' },
children: [
{
path: 'menu1-2-1',
component: () => import('@/views/nested/menu1/menu1-2/menu1-2-1'),
name: 'Menu1-2-1',
meta: { title: '菜單1-2-1' }
},
{
path: 'menu1-2-2',
component: () => import('@/views/nested/menu1/menu1-2/menu1-2-2'),
name: 'Menu1-2-2',
meta: { title: '菜單1-2-2' }
}
]
},
{
path: 'menu1-3',
component: () => import('@/views/nested/menu1/menu1-3'),
name: 'Menu1-3',
meta: { title: '菜單1-3' }
}
]
},
{
path: 'menu2',
component: () => import('@/views/nested/menu2/index'),
meta: { title: '菜單2' }
}
]
},
{ path: '*', redirect: '/404', hidden: true }
]
路由表動態(tài)生成
借助router.beforEach鉤子函數(shù)枯冈、我們可以在每次跳轉(zhuǎn)路由的時候?qū)崿F(xiàn)用戶的登錄信息的獲取毅贮、訪問權(quán)限、和登錄是否失效尘奏、是否是白名單等功能具體代碼如下
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import { getToken } from '@/utils/auth' // 驗權(quán)
const whiteList = ['/login'] // 不重定向白名單
router.beforeEach((to, from, next) => {
if (getToken()) {
if (to.path === '/login') {
next({ path: '/' })
} else {
if (store.getters.roles.length === 0) {
store.dispatch('GetInfo').then(res => { // 拉取用戶信息
const roles = res.data.roles
store.dispatch('GenerateRoutes', { roles }).then(() => { // 根據(jù)roles權(quán)限生成可訪問的路由表
router.addRoutes(store.getters.addRouters) // 動態(tài)添加可訪問路由表
next({ ...to, replace: true }) // hack方法 確保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
})
}).catch((err) => {
store.dispatch('FedLogOut').then(() => {
Message.error(err || 'Verification failed, please login again')
next({ path: '/' })
})
})
} else {
next()
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
next(`/login?redirect=${to.path}`) // 否則全部重定向到登錄頁
}
}
})
router.afterEach(() => {
})
import { asyncRouterMap, constantRouterMap } from '@/router'
/**
* 通過meta.role判斷是否與當(dāng)前用戶權(quán)限匹配
* @param roles
* @param route
*/
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
/**
* 遞歸過濾異步路由表滩褥,返回符合用戶角色權(quán)限的路由表
* @param routes asyncRouterMap
* @param roles
*/
function filterAsyncRouter(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRouter(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
const permission = {
state: {
routers: constantRouterMap,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers
state.routers = constantRouterMap.concat(routers)
}
},
actions: {
GenerateRoutes({ commit }, data) {
return new Promise(resolve => {
const { roles } = data
let accessedRouters
if (roles.includes('admin')) {
accessedRouters = asyncRouterMap
} else {
accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
}
commit('SET_ROUTERS', accessedRouters)
resolve()
})
}
}
}
export default permission
總結(jié)
今天主要是從角色方面來控制用戶的權(quán)限, 基本可以滿足大部分人的需求炫加,當(dāng)然不是萬能的瑰煎,如果某些項目要求權(quán)限的控制更加顆粒化俗孝,單憑角色是不可取的酒甸,可以實現(xiàn)只不過需要增加很多的角色和頻繁的修改路由表的配置,大家業(yè)余時間可以自己去研究發(fā)現(xiàn)更好的辦法驹针。