这里的权限只是局限于前端,只用来控制用户角色在登陆后应当显示哪些菜单,真正的权限校验配置还会在后端维护一套路由表(这里官方文档说是为了摆脱前后端耦合的支配,我觉得没必要,这个可以在开发之前先让后端开发路由增删查改接口,直接暴露给前端配置,应该耗不了多长时间,这样前端只要post个json到后端就可以完成配置。用户的惯性思维是我能看到就应当可以操作,不管是从安全的角度还是用户体验的角度, 权限都理应由后端统一控制。)。
※阅读时主要关注注释点。本文主要记录作者分析过程发现的关键点,如需详细教程请移步Github上的官方文档。
本文基于4.2.1版本国际化分支 i18n Branch代码做分析
(主干在版本v4.1.0之后已不再支持国际化,但仍会保持与主干同步更新该分支。
After the v4.1.0+ version,the default master branch will not support i18n.Please use i18n Branch, it will keep up with the master update)。
1.没登陆之前
没登陆之前只挂载不需要权限的路由,比如login之类的
1.1、main.js引入router.js
import router from './router' //这里其实只挂载了不需要权限的路由
new Vue({
el: '#app',
router,
store,
i18n,
render: h => h(App)
})
1.2、非权限路由挂载(router.js)
export const constantRoutes = [
{
path: '/redirect',
component: Layout,
hidden: true,
children: [
{
path: '/redirect/:path*',
component: () => import('@/views/redirect/index')
}
]
},
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
}]
//创建路由实例
const createRouter = () => new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes //不需要权限的路由
})
//初始化路由实例
const router = createRouter()
//导出
export default router
2.登录后
2.1、通过store中user模块下的getInfo动作获取用户角色信息
const { roles } = await store.dispatch('user/getInfo')
2.2、根据角色信息去生成动态路由列表
2.2.1、调用路由生成动作
const actions = {
......
//获取权限路由
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
// dynamically add accessible routes
//此时路由器里面只有非权限路由,现在把获取到的权限路由推入路由器
router.addRoutes(accessRoutes)
......
}
2.2.2、生成动态路由
import { asyncRoutes, constantRoutes } from '@/router' //所有路由
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
let accessedRoutes
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
} else {
//根据角色获取动态路由
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
//刷新当前权限路由
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
/**
* Use meta.role to determine if the current user has permission
* @param roles
* @param route
*/
function hasPermission(roles, route) {
//路由中有配置meta.roles才会进入权限过滤
if (route.meta && route.meta.roles) {
//判断这个路由的meta.roles角色列表是否包含当前角色
return roles.some(role => route.meta.roles.includes(role))
} else {
return true
}
}
/**
* Filter asynchronous routing tables by recursion
* @param routes asyncRoutes
* @param roles
*/
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
//判断当前角色是否有该路由的权限
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
//把有权限的路由推入动态列表
res.push(tmp)
}
})
return res
}