vue 登录权限

3,626 阅读4分钟
本文的思路主要是,点击登录,请求登录接口保存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