黑马程序员技术交流社区

标题: Vue 权限控制(路由验证) [打印本页]

作者: 小刀葛小伦    时间: 2019-8-16 16:32
标题: Vue 权限控制(路由验证)
下面介绍两种权限控制的两种方法:
路由元信息(meta)如果一个网站有不同的角色,比如管理员普通用户,要求不同的角色能访问的页面是不一样的
这个时候我们就可以把所有的页面都放在路由表里,只要在访问的时候判断一下角色权限。如果有权限就让访问,没有权限的话就拒绝访问,跳转到404页面
vue-router在构建路由时提供了元信息meta配置接口,我们可以在元信息中添加路由对应的权限,然后在路由守卫中检查相关权限,控制其路由跳转。
可以在每一个路由的 meta 属性里,将能访问该路由的角色添加到 roles 里。用户每次登陆后,将用户的角色返回。然后在访问页面时,把路由的 meta 属性和用户的角色进行对比,如果用户的角色在路由的 roles 里,那就是能访问,如果不在就拒绝访问。
代码示例1:
路由信息:
[JavaScript] 纯文本查看 复制代码
routes: [
    {
        path: '/login',
        name: 'login',
        meta: {
            roles: ['admin', 'user']
        },
        component: () => import('../components/Login.vue')
    },
    {
        path: 'home',
        name: 'home',
        meta: {
            roles: ['admin']
        },
        component: () => import('../views/Home.vue')
    },
]

页面控制:
[JavaScript] 纯文本查看 复制代码
//假设有两种角色:admin 和 user  
//从后台获取的用户角色
const role = 'user'
//当进入一个页面是会触发导航守卫 router.beforeEach 事件
router.beforeEach((to,from,next)=>{
        if(to.meta.roles.includes(role)){
                next()        //放行
        }esle{
                next({path:"/404"})        //跳到404页面
        }
})

代码示例2
当然也可以用下面的一种方法:
[JavaScript] 纯文本查看 复制代码
// router.js
// 路由表元信息
[
  {
    path: '',
    redirect: '/home'
  },
  {
    path: '/home',
    meta: {
      title: 'Home',
      icon: 'home'
    }
  },
  {
    path: '/userCenter',
    meta: {
      title: '个人中心',
      requireAuth: true // 在需要登录的路由的meta中添加响应的权限标识
    }
  }
]

// 在守卫中访问元信息
function beforeEach (to, from, next) {
  let flag = to.matched.some(record=>record.meta.requireAuth);
  //console.log(flag);  //可自己打印出来看一下
}


可以在多个路由下面添加这个权限标识,达到控制的目的
只要一切换页面,就需要看有没有这个权限,所以可以在最大的路由下 main.js 中配置
存储信息
一般的,用户登录后会在本地存储用户的认证信息,可以用 token、cookie 等,这里我们用 token。
将用户的token保存到localStorage里,而用户信息则存在内存store中。这样可以在vuex中存储一个标记用户登录状态的属性auth,方便权限控制。
代码示例


[JavaScript] 纯文本查看 复制代码
// store.js
{
  state: {
    token: window.localStorage.getItem('token'),
    auth: false,
    userInfo: {}
  },
  mutations: {
    setToken (state, token) {
      state.token = token
      window.localStorage.setItem('token', token)
    },
    clearToken (state) {
      state.token = ''
      window.localStorage.setItem('token', '')
    },
    setUserInfo (state, userInfo) {
      state.userInfo = userInfo
      state.auth = true // 获取到用户信息的同时将auth标记为true,当然也可以直接判断userInfo
    }
  },
  actions: {
    async getUserInfo (ctx, token) {
      return fetchUserInfo(token).then(response => {
        if (response.code === 200) {
          ctx.commit('setUserInfo', response.data)
        }
        return response
      })
    },
    async login (ctx, account) {
      return login(account).then(response => {
        if (response.code === 200) {
          ctx.commit('setUserInfo', response.data.userInfo)
          ctx.commit('setToken', response.data.token)
        }
      })
    }
  }
}


写好路由表和vuex之后,给所有路由设置一个全局守卫,在进入路由之前进行权限检查,并导航到对应的路由。
[JavaScript] 纯文本查看 复制代码
// router.js
router.beforeEach(async (to, from, next) => {
  if (to.matched.some(record => record.meta.requireAuth)) { // 检查是否需要登录权限
    if (!store.state.auth) { // 检查是否已登录
      if (store.state.token) { // 未登录,但是有token,获取用户信息
        try {
          const data = await store.dispatch('getUserInfo', store.state.token)
          if (data.code === 200) {
            next()
          } else {
            window.alert('请登录')
            store.commit('clearToken')
            next({ name: 'Login' })
          }
        } catch (err) {
          window.alert('请登录')
          store.commit('clearToken')
          next({ name: 'Login' })
        }
      } else {
        window.alert('请登录')
        next({ name: 'Login' })
      }
    } else {
      next()
    }
  } else {
    next()
  }
})

上述的方法是基于jwt认证方式,本地不持久化用户信息,只保存token,当用户刷新或者重新打开网页时,进入需要登录的页面都会尝试去请求用户信息,该操作在整个访问过程中只进行一次,直到刷新或者重新打开,对于应用后期的开发维护和扩展支持都很好。
动态加载菜单和路由(addRoutes)有时候为了安全,我们需要根据用户权限或者是用户属性去动态的添加菜单和路由表,可以实现对用户的功能进行定制。vue-router提供了addRoutes()方法,可以动态注册路由,需要注意的是,动态添加路由是在路由表中push路由,由于路由是按顺序匹配的,因此需要将诸如404页面这样的路由放在动态添加的最后。
代码示例
[JavaScript] 纯文本查看 复制代码
// store.js
// 将需要动态注册的路由提取到vuex中
const dynamicRoutes = [
  {
    path: '/manage',
    name: 'Manage',
    meta: {
      requireAuth: true
    },
    component: () => import('./views/Manage')
  },
  {
    path: '/userCenter',
    name: 'UserCenter',
    meta: {
      requireAuth: true
    },
    component: () => import('./views/UserCenter')
  }
]

vuex中添加userRoutes数组用于存储用户的定制菜单。在setUserInfo中根据后端返回的菜单生成用户的路由表。
[JavaScript] 纯文本查看 复制代码
// store.js
setUserInfo (state, userInfo) {
  state.userInfo = userInfo
  state.auth = true // 获取到用户信息的同时将auth标记为true,当然也可以直接判断userInfo
  // 生成用户路由表
  state.userRoutes = dynamicRoutes.filter(route => {
    return userInfo.menus.some(menu => menu.name === route.name)
  })
  router.addRoutes(state.userRoutes) // 注册路由
}


修改菜单渲染
[JavaScript] 纯文本查看 复制代码
// App.vue
<div id="nav">
  <router-link to="/">主页</router-link>
  <router-link to="/login">登录</router-link>
  <template v-for="(menu, index) of $store.state.userInfo.menus">
    <router-link :to="{ name: menu.name }" :key="index">{{menu.title}}</router-link>
  </template>
</div>








欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2