本文的思路主要是,点击登录,请求登录接口保存token,登录成功后,通过token请求用户信息接口获取用户角色roles,将用户角色与route.js路由表的角色对比,过滤出符合条件的路由,最后通过router.addRoutes挂载路由。(注:需要用到vuex,不太清除的小伙伴可以稍微看一下vuex,大致明白什么作用就好)
登录
handleLogin() { this.$refs.loginForm.validate(valid => { if (valid) { this.loading = true; this.$store .dispatch("user/login", this.loginForm) .then(() => { //vuex利用dispatch提交actions this.$router.push({ path: this.redirect || "/" }); this.loading = false; }) .catch(() => { this.loading = false; }); } else { console.log("error submit!!"); return false; } }); }以上代码更新后被自动调整为一行,不方便阅读,不知道怎么回事,有大佬知道怎么回事吗?因此为方便阅读,以上代码截图为:
其中this.$store.dispatch('user/login', this.loginForm)提交的是vuex store中user.js中的
// user login login({ commit }, userInfo) { const { username, password } = userInfo return new Promise((resolve, reject) => { login({ username: username.trim(), password: password }).then(response => {//请求登录接口成功以后 const { data } = response commit('SET_TOKEN', data.token) //vuex保存token,可方便的在其他页面获取到token setToken(data.token) //在auth.js中封装的方法,用于讲token存储在cookie中 resolve() }).catch(error => { reject(error) }) }) }以上代码截图为:
注:在刷新页面的时候,vuex数据是重新初始化,因此每次vuex的token需要赋初始值,即获取cookie的token。
以上就是登录主要涉及的内容。
获取用户信息
跳转页面之前,我们需要通过router.beforeEach来处理相关内容,这里面的内容很关键。
首先,vuex action定义获取用户信息的操作
// get user info getInfo({ commit, state }) { return new Promise((resolve, reject) => { getInfo(state.token).then(response => {//请求用户信息接口 const { data } = response if (!data) { reject('Verification failed, please Login again.') } const { roles, name, avatar, introduction } = data // roles must be a non-empty array if (!roles || roles.length <= 0) { reject('getInfo: roles must be a non-null array!') } //vuex提交相应的用户信息 commit('SET_ROLES', roles)//保存用户角色 commit('SET_NAME', name) commit('SET_AVATAR', avatar) commit('SET_INTRODUCTION', introduction) resolve(data) }).catch(error => { reject(error) }) }) }以上代码截图为:
权限
接下来为关键,包括获取用户角色,挂载路由等
router.beforeEach(async(to, from, next) => { // start progress bar NProgress.start() //页面加载进度条 const hasToken = getToken() if (hasToken) { if (to.path === '/login') {//如果存在token,并且要跳转到登录页面,则重定向到首页 next({ path: '/' }) NProgress.done() } else { //判断是否有角色信息 const hasRoles = store.getters.roles && store.getters.roles.length > 0 if (hasRoles) { next() } else {
try { // get user info // note: roles must be a object array! such as: ['admin'] or ,['developer','editor'] const { roles } = await store.dispatch('user/getInfo')//获取用户信息,并得到用户角色,roles为一个数组,如['developer','editor'] // generate accessible routes map based on roles const accessRoutes = await store.dispatch('permission/generateRoutes', roles)//获取跟roles比对以后可用于挂载的路由 // dynamically add accessible routes router.addRoutes(accessRoutes)//挂载路由 // hack method to ensure that addRoutes is complete // set the replace: true, so the navigation will not leave a history record next({ ...to, replace: true }) } catch (error) {
// remove token and go to login page to re-login await store.dispatch('user/resetToken') Message.error(error || 'Has Error') next(`/login?redirect=${to.path}`) NProgress.done() } } } } else { /* has no token*/ if (whiteList.indexOf(to.path) !== -1) { // in the free login whitelist, go directly next() } else { // other pages that do not have permission to access are redirected to the login page. next(`/login?redirect=${to.path}`) NProgress.done() } }})以上代码截图为:
以上代码中的:
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
//获取跟roles对比以后可用于挂载的路由主要涉及的vuex action 及其所用过滤方法为
const actions = { generateRoutes({ commit }, roles) { return new Promise(resolve => { let accessedRoutes if (roles.includes('admin')) {//如果角色是admin,有权访问所有页面 accessedRoutes = asyncRoutes || [] } else { accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)//其他角色通过这个方法对比路由信息,过滤出有权访问的路由 } commit('SET_ROUTES', accessedRoutes)//最终获得的可访问的路由,通过router.addRoutes挂载 resolve(accessedRoutes) }) }}function hasPermission(roles, route) { if (route.meta && route.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}以上代码截图为:
最后,router.js如下,将不需要权限的页面比如登录页写在constantRoutes中,讲需要权限的页面写在asyncRouterMap中。
注意事项:页面一定要最后加载,如果放在constantRoutes一同声明了404,后面的所以页面都会被拦截到404
import Vue from 'vue'import Router from 'vue-router'Vue.use(Router)
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 } { path: '/404', component: () => import('@/views/error-page/404'), hidden: true }]
//异步挂载的路由
//动态需要根据权限加载的路由表
export const asyncRouterMap = [
{
path: '/permission',
component: Layout,
name: '权限测试',
meta: { role: ['admin','super_editor'] }, //页面需要的权限
children: [
{
path: 'index',
component: Permission,
name: '权限测试页',
meta: { role: ['admin','super_editor'] } //页面需要的权限
}]
},
{ path: '*', redirect: '/404', hidden: true }
];const router = createRouter()export function resetRouter() { const newRouter = createRouter() router.matcher = newRouter.matcher // reset router}export default router
主要文件
store/modules/user.js 主要作用vuex 登录,登出,获取用户信息
src/permission.js 主要作用router.beforeEach的处理,获取用户信息(角色),获取过滤后的路由表,挂载路由等,以及一系列跳转页面前的判断
store/modules/permission.js 主要作用vuex 具体的获取可访问路由
router.js 路由表
参考文章:手摸手,带你用 vue 撸后台 系列二(登录权限篇)
参考完整项目地址:vue-element-admin