看不懂你来锤我之vue RBAC权限之页面级权限管理设置流程

目录

前置

什么是RBAC权限设计思想

页面级权限实现流程

分析

1 设置路由router/index.js

2 获取权限数据 过滤得到动态路由表

3 动态路由表加载router, 渲染左侧菜单

4 bug 解决


前置

你需要先完成 给用户分配角色, 给角色分配权限的基础前置, 并且你可以从后端/mock 获取到该用户的权限信息.


什么是RBAC权限设计思想

通俗的讲, 假如你是一位职业棋手(角色), 那你可以进入中国棋院(权限), 你的朋友虽然是你的朋友(角色), 但他不是职业棋手, 中国棋院不让外人进, 他不能进入(权限)

有朝一日, 你朋友疯狂内卷, 一天24小时加班, 得到了职业棋手认证(角色), 他现在可以进中国棋院了(权限). 

总之, 如果我一个一个的给员工添加权限是不是太麻烦了? 往往公司都有一套规则, a角色拥有a类权限, b角色拥有b权限, 我想给新来的员工添加权限, 直接根据老板指示给他个角色就行

角色就是一堆权限的集合


页面级权限实现流程

分析

首先路由守卫里是登录后, 有token, 进入else里面的提交vuex actions异步获取userInfo, vuex的返回值里里面有权限数据

 

1 设置路由router/index.js

首先我们知道, 页面权限跟路由router有关, 其中路由表是控制能不能跳转到对应页面组件的关键. 所以这8个动态路由我们需要动态设置, 根据menus设置

router/index.js

可以看到, 我们这里是写死了, 所以我们每个用户都可以访问每个页面, 这里直接删除 ... asyncRoutes

2 获取权限数据 过滤得到动态路由表

接下来,我们要建立后端返回的menus和动态路由表的联系, 想想我们最先获得menus的地方, 是permission路由守卫提交的actions请求userInfo,里面带有menus. 而dispatch返回的是个promise对象, 所以我们可以在路由守卫 await 这个值

扩展: vuex官网 => actions 里面return promise对象带需要的值 这里也可以直接return 需要的值. 因为vuex官网这样说的首先,你需要明白 store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 Promise:

vuex里面返回值

路由守卫里 取值

打印可以在路由守卫里得到最早的menus

接下来就是在这里 我们要做一个事, 将写好的 的动态路由表拿过来, 按照menus 过滤得到新的路由表

我每个动态路由的对象是这样的, 可以看到既可以用path属性取2位之后的字符串进行filter, 也可以直接用name, 我用path判断

 路由守卫里

const { menus } = await store.dispatch('user/getInfo')
const filterAsyncRoutes = asyncRoutes.filter(item => menus.includes(item.path.slice(1)))

 得到了过滤权限后的动态路由表

3 动态路由表加载router, 渲染左侧菜单

路由守卫里面 得到新的动态路由表了, 接下来要做两件事

  1. 加载到router
  2. 拼接动态路由和静态路由 , 渲染左侧菜单

我这里采用vuex 存储动态路由和静态路由, 然后加载到router, 因为左侧菜单是其他组件, 等会按照路由表渲染可以跨组件使用, 具体方法就是在上面路由守卫得到需要的数据后, 直接提交mutations, vuex里面导入静态路由表, mutations里面拼接 静态和过滤后的动态路由表 生成最终的totalRoutes

在路由守卫获取上面的值后, 立即加载动态路由到router.addRoutes方法

const { menus } = await store.dispatch('user/getInfo')
const filterAsyncRoutes = asyncRoutes.filter(item => menus.includes(item.path.slice(1)))
router.addRoutes(filterAsyncRoutes)
store.commit('menu/loadRoute', filterAsyncRoutes)

vuex代码 路由守卫提交mutations 传过滤的动态路由表 导入静态路由表, 合成最终路由表用来渲染左侧菜单

分析左侧菜单逻辑

siderbar组件的index.vue

原本是通过this.$router.options.routes 也就是我们在router/index.js 配置的路由表, 而我们动态添加的路由表是不会自动更新到路由表的, this.$router.options.routes 是拿到初始化时配置的路由规则, 所以这也是为什么我要自己存到vuex 这里也是vue故意这么设计的, 作者曾在issue回答过: options is the object passed to the vuerouter constructor. It's not modified afterwards.

但是作者不建议用上面的方法, 最好是存到vuex里面做

接着左侧菜单设置 更改代码 从vuex获取我们动态设置后的路由表

左侧菜单渲染完成

4 bug 解决

bug1: 刷新404

在动态路由页面, 比如上面的department 组织架构里面,我们本来是正常可以访问的, 但是只要刷新, 就会发现404的问题

因为我们静态路由表的最后的规则是*通配符到404, 刷新时,动态路由需要重新挂载到路由实例,但是还没挂载就被通配符拦截到404页面

解决办法: 404从静态路由表中删除, 添加到过滤后的动态路由表最后面, 也就是push进去404这条规则

bug2: 刷新空白 /页面不加载不报错

嘿嘿 解决了bug1 是不是发现又多了一个bug 刷新页面不加载不报错,空白

这又是为啥呢, 我第一次碰到的时候也纳闷, 后来经过查阅资料, 大概原因是我们在守卫里面addRoutes, 还没执行完, 就执行了next() 所以我们得要加个next(to.path) 重新进入一次守卫, 这时动态路由已经添加进去了

bug3: 退出登录再登录,控制台出现重复添加routes的信息,而且没有权限的账户只要不刷新页面, 可以通过地址栏输入地址,访问原本没有权限的地址

因为我们退出时没有重置路由, 这里最严重的就是权限混乱, 因为前面的用户动态路由信息没有清空, 造成权限泄漏, 所以在退出登录时重置路由

一般有三个地方需要设置

// router/index.js 定义重置路由方法
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher // reset router
}

// vuex登出actions中 最后重置路由
// 以及一切你知道的问题处 比如token失效时一般我们放到request拦截器里面处理
import { resetRouter } from '@/router'

logout(context) {
      context.commit('updateToken', '')
      context.commit('setUserInfo', {})
      resetRouter()
}

// 我的拦截器中设置当401 status时 提交actions 登出 所以我只用设置上面vuex那段
// 响应拦截器
async error => {
    // console.log('响应拦截器error')
    if (error.response.status === 401) {
      await store.dispatch('user/logout')
      router.push(`/login?redirect=${router.currentRoute.fullPath}`)
    }
    Message.error(error.response.data.message)
    return Promise.reject(error)
}

bug4: 由于我们做了个退出登录或者token过期会自动往地址栏加原网页地址的path信息, 如果我们退出登录后, 登录没有原网页权限的用户, 会出现404 在bug2 处的next(to.path) 加个判断,如果不在menus里面 直接跳转主页

menus.includes(to.path.substring(1)) ? next(to.path) : next('/')

menus: 后端返回的权限数组 元素是页面标识


至此, 基本上页面权限管理基本做完了, 你学废了吗... 

猜你喜欢

转载自blog.csdn.net/qq_59650449/article/details/128587269
今日推荐