vue后台登录权限管理代码详解

前言

代码来自:Gitee 地址
在线预览:Gitee 在线预览
手摸手,带你用vue撸后台 系列二(登录权限篇)
花裤衩大佬的这篇文章详细地介绍了如何使用vue实现权限管理(写于17年,与本文所用代码有些许不同)。main.js 部分代码涉及 store 和 router 的反复调用。初学者阅读代码可能会比较困难。为了使初学者能够更加轻松地阅读代码,我将 main.js 中调用的函数、计算属性等的源代码添加到 main.js 代码中,并且自己添加了注释。代码如下:

main.js 详解

router.beforeEach(async (to, from, next) => {
  // start progress bar
  NProgress.start()

  // set page title
  document.title = getPageTitle(to.meta.title)

  // determine whether the user has logged in
  const hasToken = getToken()

  if (hasToken) {
    if (to.path === '/login') {
      // 如果已经登录则重定向到首页
      next({ path: '/' })
      NProgress.done()
    } else {
      // 判断当前用户是否已获取到 user_info 信息
      const hasRoles = store.getters.roles && store.getters.roles.length > 0
      if (hasRoles) {
        next()
      } else {
        // 未获取到 user_info 信息
        try {
          // 获取 user_info
          // 权限列表必须是一个数组 例如: ['admin'] 或者 ['developer','editor']
          const { roles } = await store.dispatch('user/getInfo')
          // store.dispatch('user/getInfo') 代码如下:
          getInfo({ commit, state }) {
            return new Promise((resolve, reject) => {
              getInfo(state.token).then(response => {
                // getInfo 代码如下:
                // 携带 token 发送 get 请求来获取 user_info
                function getInfo(token) {
                  return request({
                    url: '/vue-element-admin/user/info',
                    method: 'get',
                    params: { token }
                  })
                }
                // getInfo 代码结束

                const { data } = response
                if (!data) {
                  // 如果data不存在则报错
                  reject('Verification failed, please Login again.')
                }
                // 解构出data中的 roles, name, avatar, introduction
                const { roles, name, avatar, introduction } = data
                // 权限列表必须是一个非空数组
                if (!roles || roles.length <= 0) {
                  // 如果为空数组则报错
                  reject('getInfo: roles must be a non-null array!')
                }
                // 将获取到的user_info存在state中
                commit('SET_ROLES', roles)
                commit('SET_NAME', name)
                commit('SET_AVATAR', avatar)
                commit('SET_INTRODUCTION', introduction)
                // 上述4行代码如下:
                SET_ROLES: (state, roles) => {
                  state.roles = roles
                }
                SET_NAME: (state, name) => {
                  state.name = name
                },
                SET_AVATAR: (state, avatar) => {
                  state.avatar = avatar
                },
                SET_INTRODUCTION: (state, introduction) => {
                  state.introduction = introduction
                }
                // 4行代码结束

                resolve(data)
              }).catch(error => {
                reject(error)
              })
            })
          }
          //生成当前角色可访问的路由表
          const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
          // store.dispatch('permission/generateRoutes', roles)代码如下:
          generateRoutes({ commit }, roles) {
            return new Promise(resolve => {
              let accessedRoutes
              if (roles.includes('admin')) {
                // 如果roles包含admin,则accessedRoutes等于asyncRoutes
                // asyncRoutes为router中定义的所有需要权限的路由表
                // 也就是说admin拥有访问所有页面的权限
                accessedRoutes = asyncRoutes || []
              } else {
                //过滤出拥有roles权限的路由
                accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
                // filterAsyncRoutes代码如下:
                function filterAsyncRoutes(routes, roles) {
                  const res = []
                  //遍历需要权限的路由表
                  routes.forEach(route => {
                    const tmp = { ...route }
                    if (hasPermission(roles, tmp)) {
                      // hasPermission 代码如下:
                      function hasPermission(roles, route) {
                        if (route.meta && route.meta.roles) {
                          // 如果route中有roles权限则返回true,都没有则返回false
                          return roles.some(role => route.meta.roles.includes(role))
                        } else {
                          return true
                        }
                      }
                      // hasPermission 代码结束

                      //如果tmp中存在子路由
                      if (tmp.children) {
                        //递归 为tmp添加children属性(相当于添加子路由)
                        //属性值为含有该roles权限的路由
                        tmp.children = filterAsyncRoutes(tmp.children, roles)
                      }
                      // 将含有roles权限的路由添加到res中
                      res.push(tmp)
                    }
                  })
                  // 返回所有含有roles权限的路由的集合
                  // accessedRoutes = res
                  return res
                }
                // filterAsyncRoutes代码结束

              }
              commit('SET_ROUTES', accessedRoutes)
              // SET_ROUTES 代码如下:
              SET_ROUTES: (state, routes) => {
                state.addRoutes = routes
                // 将不需要权限的路由和需要权限的路由拼接到一起
                // constantRoutes为router中不需要权限的路由表
                state.routes = constantRoutes.concat(routes)
              }
              // SET_ROUTES 代码结束

              resolve(accessedRoutes)
            })
          }
          // generateRoutes 代码结束

          //动态添加可访问路由表
          router.addRoutes(accessRoutes)

          // hack方法 确保addRoutes已完成 设置replace: true 所以导航不会留下历史记录
          next({ ...to, replace: true })
        } catch (error) {
          // 删除token回到登录页面重新登录
          await store.dispatch('user/resetToken')
          Message.error(error || 'Has Error')
          next(`/login?redirect=${to.path}`)
          NProgress.done()
        }
      }
    }
  } else {
    /* 没有 token*/
    if (whiteList.indexOf(to.path) !== -1) {
      // 在免登录白名单中继续跳转
      next()
    } else {
      // 否则全部重定向到登录页
      next(`/login?redirect=${to.path}`)
      NProgress.done()
    }
  }
})

main.js 原文

router.beforeEach(async(to, from, next) => {
  // start progress bar
  NProgress.start()

  // set page title
  document.title = getPageTitle(to.meta.title)

  // determine whether the user has logged in
  const hasToken = getToken()

  if (hasToken) {
    if (to.path === '/login') {
      // if is logged in, redirect to the home page
      next({ path: '/' })
      NProgress.done()
    } else {
      // determine whether the user has obtained his permission roles through getInfo
      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')

          // generate accessible routes map based on roles
          const accessRoutes = await store.dispatch('permission/generateRoutes', 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()
    }
  }
})

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
  },
  {
    path: '/404',
    component: () => import('@/views/error-page/404'),
    hidden: true
  }
]

//实例化vue的时候只挂载constantRouter
export default new Router({
  routes: constantRouterMap
});

//异步挂载的路由
//动态需要根据权限加载的路由表 
export const asyncRoutes = [
  {
    path: '/permission',
    component: Layout,
    redirect: '/permission/page',
    alwaysShow: true, // will always show the root menu
    name: 'Permission',
    meta: {
      title: 'Permission',
      icon: 'lock',
      roles: ['admin', 'editor'] // you can set roles in root nav
    },
    children: [
      {
        path: 'page',
        component: () => import('@/views/permission/page'),
        name: 'PagePermission',
        meta: {
          title: 'Page Permission',
          roles: ['admin'] // or you can only set roles in sub nav
        }
      },
      {
        path: 'directive',
        component: () => import('@/views/permission/directive'),
        name: 'DirectivePermission',
        meta: {
          title: 'Directive Permission'
          // if do not set roles, means: this page does not require permission
        }
      },
      {
        path: 'role',
        component: () => import('@/views/permission/role'),
        name: 'RolePermission',
        meta: {
          title: 'Role Permission',
          roles: ['admin']
        }
      }
    ]
  }
]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值