RBAC权限设计-后台管理-页面级别权限应用 - 使用动态添加路由addRoutes方法会出现的 Bug 及解决方案

1,102 阅读3分钟

RBAC权限设计-后台管理-页面级别权限应用 - 使用动态添加路由addRoutes方法会出现的 Bug 及解决方案

为了达成不同的帐号登陆系统后能看到不同的页面,能执行不同的功能的目标,我们有很多种解决方案,RBAC(Role-Based Access control)权限模型 ,也就是基于角色的权限分配解决方案,那我们就来聊聊权限应用那些事吧

根据角色来动态添加路由,可以使用相关的 API,--- addRoutes() 动态添加路由的方法

addRoutes 基本使用

router.addRoutes([路由配置对象])
或者:
this.$router.addRoutes([路由配置对象])

示例

// 按钮
<button @click="hAddRoute">addRoute</button>

// 回调
hAddRoute() {
	this.$router.addRoutes([{
		path: '/abc',
		component: () => import('@/views/abc'),
	}])
},

点击了按钮之后,就可以在地址中访问 /abc 页面了

  1. 先把 router 文件夹里面路由配置规则里面的直接添加的动态路由删除

    const createRouter = () => new Router({
      // mode: 'history', // require service support
      scrollBehavior: () => ({ y: 0 }),
      // routes: constantRoutes
      // 合并动态和静态的路由  , ...asyncRoutes
    - routes: [...constantRoutes, ...asyncRoutes]
    + routes: [...constantRoutes]
    })
    
  2. 在路由前置守卫里面来动态添加 router.addRoutes([路由配置对象]) 路由配置对象,这样的结果就是在地址栏里面输入路由地址还可以访问页面,菜单栏里面会看不到动态路由了(问题一)

    路由前置守卫,有 token 进入首页时

    // router.addRoutes([路由配置对象])
    // 引入静态路由
    import { constantRoutes } from '@/router/index'
    
    // 根据角色作用有的权限来 过滤动态路由
    const menus = store.state.user.userInfo.roles.menus
    const filterasyncRoutes = asyncRoutes.filter(item => {
      return menus.includes(item.children[0].name)
    })
            
    router.addRoutes(filterasyncRoutes)
    
    await store.commit('menus/setMenusList', filterasyncRoutes)
    
  3. (问题一解决方案) 可以把静态路由与动态路由合并存到 vuex 里面,通过 vuex 来生成菜单栏,不同的角色所拥有的权限不同,因此进入页面能看到的动态路由也会不同

    // 引入静态路由 constantRoutes
    import { constantRoutes } from '@/router/index'
    
    export default {
      namespaced: true,
      state() {
        return {
          menusList: [...constantRoutes]
        }
      },
      mutations: {
        setMenusList(state, val) {
          state.menusList = [...constantRoutes, ...val]
        }
      }
    }
    

使用 addRoutes 方法会有以下 bug

一、刷新浏览器回跳到404页面

原因

{ path: '*', redirect: '/404', hidden: true }

404页面在静态路由里面,动态路由拼接到动态路由后面,因此会先进入404页面

解决方案

把静态路由里面的 404路由规则删除 添加到动态路由最后面

// router.addRoutes([路由配置对象])
// 引入静态路由
import { constantRoutes } from '@/router/index'

// 根据角色作用有的权限来 过滤动态路由
const menus = store.state.user.userInfo.roles.menus
const filterasyncRoutes = asyncRoutes.filter(item => {
  return menus.includes(item.children[0].name)
})

// 把 404 页面添加到最后面
filterasyncRoutes.push({ path: '*', redirect: '/404', hidden: true })
        
router.addRoutes(filterasyncRoutes)

await store.commit('menus/setMenusList', filterasyncRoutes)

二、刷新页面会白屏

解决方案

在路由前置守卫里面的 next() 里面添加

if (store.state.user.userInfo.userId) {
  next()
} else {
  // ...     
    
  // 解决刷新出现的白屏bug  
  next({
    ...to, // next({ ...to })的目的,是保证路由添加完了再进入页面 (可以理解为重进一次)
    replace: true // 重进一次, 不保留重复历史
  })
}

三、菜单异常-控制台报错路由重复

原因

路由设置是通过router.addRoutes(filterRoutes)来添加的,退出时,并没有清空,再次登陆,又加了一次,所以有重复。

需要将路由权限重置 (恢复默认) 将来登录后再次追加才可以,不然的话,就会重复添加

解决方案

// 重置路由
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // 重新设置路由的可匹配路径
}

这个方法就是将路由重新实例化,相当于换了一个新的路由,之前**加的路由就不存在了,需要在登出的时候, 调用一下即可**