vue权限问题解决方案

前言

最近一直在忙着一个用vue来作的权限管理的项目,其实在此以前,我也研究过vue的权限如何实现,而且也为之写过一篇博客,但当真正应用在项目中的时候,仍是发现了许多问题,因此此篇也会就着我在项目中遇到的一些问题,拿出来和你们分享一下,固然示例代码仍是个人github仓库中的ant-design-vue-msvue

权限问题解决思路

对于一个先后端分离的项目而言,权限再也不是仅仅靠后端来控制,后端只能控制接口的权限,前台的页面显示仍是须要咱们来控制,针对vue的项目,首先我想的是当权限很少,而且都是单个权限的状况下,咱们彻底没有必要使用vue中提供的addRoutes的方法,可使用动态组件来作,即咱们根据后端返回的角色,来细度控制动态组件的显示内容,所谓动态组件其实就是vue内置提供的component组件webpack

<component :is="currentComponent"/>
复制代码

相信看到这里,熟悉的同窗应该已经想起来了,这样的话,咱们就不须要用到vuex,以及路由配置等等复杂的问题,单纯靠后台返回的角色名称就能解决全部的问题了,看到这里是否是以为今天的内容就这些了,别着急,下面还有“好看的”。git

权限设置中的问题

这样虽然能解决一些简单权限的问题,可是针对稍微复杂一些的权限应用,就显得有些力不从心了,当角色过多,而且还包含了混合角色的权限的话,则会衍生出不少问题,这里也是列举我遇到的一些问题,同窗们能够细细推敲一下。github

  • 若是是混合角色的话,动态组件的路由跳转实际都是跳转到一个页面,可是混合角色确定会一个页面中跳到不一样角色的页面,这样可能咱们要多写不少层的判断,权限混合越多,就越难以去判断。
  • 动态组件扩展性比较差,若是咱们再添加一个权限呢,就要再多加一个动态组件的内容,而且出现混合权限的话,那改动的地方就更多了

因此综上所述,最终我仍是选择了传统的addRoutes,那么确定会有同窗问了,既然这个方案不行,那干吗还要用呢。问得好,其实动态组件就是一种尝试,只有知道错了,不知足需求了,咱们才能更知道为何会去使用传统的addRoutes的权限方案。web

权限问题解决方法

因此咱们来看看addRoutes带来的一些“好处”:vuex

  • 一次配置,多处使用,咱们配置好了动态路由之后,不论后期添加多少权限,都能很好的显示路由跳转等等,而且也不须要改动代码,只须要添加新增角色的模块就能够了。
  • 遇到混合角色的问题,若是内容布局相似的话,咱们可使用自定义指令来区分要显示的模块,这样的话若是一个帐号同时拥有不少角色的话,那么包含这个角色的模块则会相应的显示出来,就不会出现须要判断究竟显示哪一个模块了,也不须要单独为某个角色去设置一个页面来显示了。

相信作过权限的同窗对上面的内容仍是有一些心得的,而后咱们按照该有的步骤一步一步来,这些步骤在上面个人github中已经有了,你们能够对照一下。后端

  1. 全局导航守卫的设置,此处设置全局导航守卫,我以为更可能是为了数据持久化,你们都知道,vuex虽然很是好用,可是会有刷新丢失数据的状况,所以针对这种状况,咱们使用导航守卫,每次刷新的时候,会从新请求后台的接口来获取角色信息。
if (store.getters.roles.length === 0) {
        store
          .dispatch('GetInfo')
          .then(res => {
            const roles = res.data.resultData && res.data.resultData.roles
            store.dispatch('GenerateRoutes', { roles }).then(() => {
              // 根据roles权限生成可访问的路由表
              // 动态添加可访问路由表
              router.addRoutes(store.getters.addRouters)
            })
          })
          .catch(() => {
            store.dispatch('Logout').then(() => {
              next({ path: '/user/login', query: { redirect: to.fullPath } })
            })
          })
      } else {
        next()
      }
复制代码

这里代码作了简化,主要给你们看下上面会有一个角色判断长度,主要是当咱们不刷新的状况,页面角色信息不回丢失,所以咱们也就没有必要去请求后台获取角色信息了,来节省请求数量。 2. 经过上面的代码能够看到,咱们首先是请求的角色信息,而后请求了生成路由的GenerateRoutes的方法,方法是写在vuex中的action里面的,这部分的内容由于网上有不少教程,其实主要概括一下,就是对路由进行递归过滤,过滤出符合角色的路由,而后将静态路由和过滤出来的动态路由连接起来bash

const permission = {
  state: {
    routers: constRouterMap,
    addRouters: []
  },
  mutations: {
    SET_ROUTERS: (state, routers) => {
      state.addRouters = routers
      state.routers = constRouterMap.concat(routers)
    }
  },
  actions: {
    GenerateRoutes({ commit }, data) {
        //略
    }
  }
}
复制代码
  1. 设置咱们的路由文件,这部分放到这里来讲,主要由于这里还有个小坑,因此也是特意拿出来和你们分享一下
export const constRouterMap = [
    {
        path: '/',
        redirect: '/index',
        component: BasicLayout,
        children: [
            {
                path: '/index',
                name: 'index',
                // route level code-splitting
                // this generates a separate chunk (about.[hash].js) for this route
                // which is lazy-loaded when the route is visited.
                component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue'),
                meta: {
                    title: '仪表盘'
                }
            },
            {
                path: '/home',
                name: 'home',
                component: () => import(/* webpackChunkName: "home" */ '@/views/Home.vue'),
                meta: {
                    title: '表单页'
                }
            },
            {
                path: '/pattern',
                name: 'pattern',
                component: () => import(/* webpackChunkName: "pattern" */ '@/views/DesignPattern.vue')
            },
            {
                path: '/map',
                name: 'map',
                component: () => import(/* webpackChunkName: "map" */ '@/views/DataMap.vue'),
                meta: {
                    title: '地图组件'
                }
            },
        ]
    },
    {
        path: '/user',
        redirect: '/login',
        component: UserLayout,
        children: [
            {
                path: '/login',
                name: 'login',
                component: () => import(/* webpackChunkName: "login" */ '@/views/user/Login.vue')
            },
            {
                path: '/register',
                name: 'register',
                component: () => import(/* webpackChunkName: "login" */ '@/views/user/Register.vue')
            }
        ]
    },
    //须要注意这里,404的路由必定要写在静态路由中
    {
        path: '/404',
        component: () => import(/* webpackChunkName: "not_found" */ '@/views/NotFound.vue')
    }
]

export const asyncRouterMap = [
    {
        path: '/',
        redirect: '/index',
        component: BasicLayout,
        children: [
            {
                path: '/controls',
                name: 'controls',
                component: () => import(/* webpackChunkName: "controls" */ '@/views/Controls.vue'),
                meta: {
                    title: '权限设置',
                    permission: ['admin']
                }
            }
        ]
    },
    //捕获未定义的路由配置
    {
        path: '*',
        redirect: '/404',
        hidden: true
    }
]

复制代码

上面关于404页面的定义顺序很是重要,若是在静态路由中定义了捕获的路由path:"*",而在动态路由中定义了404路由的话,则当导航钩子中判断比较复杂的话,会出现一些意想不到的错误,我就是当时写反了顺序,而且还在导航钩子中作了一些复杂的面包屑的判断,一旦刷新页面的话,则会出现如下错误前后端分离

这种错误的产生,多是由于刷新时,导航钩子发现动态添加进来的路由找不到一直进行获取动态路由的方法,致使最后调用栈溢出所致使,所以你们在使用的时候必定要很是当心。 4. 当咱们生成路由后,退出应用的切换新的角色帐号进行登陆时,必定要记得的两件事,第一就是清空vuex里面的角色信息,在不刷新的状况下,这些信息是不会丢失的,当不一样角色的帐号登陆时,原来的角色依然存在,那么确定会出现问题,其次则是在跳转会登陆页的时候,须要设置刷新页面的代码

window.location.reload();
this.$router.push({name: 'login'});
复制代码

先刷新之后再跳转到登陆页,这个则是由于addRoutes生成的路由在不刷新的状况下会一直存在,即便下个不一样角色的帐号登陆时,依然会拿以前存在的路由信息去进行过滤,这样过滤的结果必然是当前角色的路由一个都不存在,所以生成的路由信息仍是上个角色的路由,因此在完成了以前这些步骤时,必定不要忘记了作这两步,这样也才是一个完整的权限解决方案async

尾声

以上也是我在项目中一些收货吧,拿出来和你们分享,也是但愿你们少走一些弯路,留心咱们开发中遇到的每一个看似很小的问题,其实每每是咱们最后解决问题的关键,不管是从动态组件仍是动态路由,问题的出现也是咱们不断去完善本身的过程。