权限设计思路
- 在本项目中,使用了RBAC模式的权限设计,即设计不同的角色,给角色添加对应的权限页面,那么只需要记录登陆用户的角色属性,就可以给到相对应的权限
- 操作逻辑流程:会给用户分配对应的角色,在角色中可以分配对应的权限,权限则可以通过权限管理点操作增加删除编辑的详细操作
- 先给用户分配角色
2.再给角色分配权限
3. 在权限管理点可以操作权限的增删改
权限分配的具体实现
- 实现方法:在给用户分配了角色,给角色分配了权限后,我们就可以对权限进行管理,在权限管理点,有标识,该标识是和项目的路由模块相对应的,如果该权限拥有对应的标识那么就可以拥有对应的路由模块,也就可以进入到对应的页面,筛选出标识对应的路由模块,使用vue-router提供的addRoutesAPI方法,动态添加路由规则
- 实现逻辑:在用户登陆时,获取用户资料中的对应标识,通过标识和路由模块进行对比筛选出符合权限的路由模块,然后通过addRoutes动态添加路由规则
在vuex中实现路由模块的筛选
- vuex代码逻辑:在state中定义初始化静态路由,在mutations中通过参数接受筛选后的动态路由规则,并且和静态路由合并,在actions中接收资料中定义的标识,然后通过和标识的对比,筛选出所拥有的动态路由权限
- 代码
// constantRoutes是默认都拥有的静态路由
// asyncRoutes谁所有的动态路由模块
// 都定义在了脚手架的路由文件中
import { constantRoutes, asyncRoutes } from '@/router'
const state = {
// 一开始就有静态路由的权限
routes: constantRoutes // 路由表 表示当前用户所有的路由表
}
const mutations = {
// 定义修改routers的mutations
// newRoutes这个参数指已经筛选后的动态路由
setRoutes(state, newRoutes) {
state.routes = [...constantRoutes, ...newRoutes] //让静态路由和筛选后的动态路由合并
}
}
const actions = {
// 筛选权限路由
// 第二个参数为当前用户拥有的菜单权限menus 就是后端穿过来的用户资料里用来做权限设计的标识
filterRoutes(context, menus) {
const routes = []
// filter筛选动态路由
menus.forEach(key => {
// key是标识
routes.push(...asyncRoutes.filter(item => item.name === key)) // 得到一个用户权限的路由列表
})
// includes筛选
// asyncRoutes.forEach(item => {
// if (menus.includes(item.name)) {
// routes.push(item)
// }
// })
// filter+includesd组合过滤
// routes = asyncRoutes.filter(item => menus.includes(item.name))
context.commit('setRoutes', routes)
return routes // state数据是为了显示菜单 return是为了给addRoutes添加使用
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
在路由守卫中调用Vuex实现权限分配
- 实现逻辑:由于之前在路由守卫中就通过vuex获取过用户资料,而筛选路由模块所需的标识,就在用户资料中,所以在获取用户资料的vuex中return了用户资料,在路由守卫调用时接收数据,然后再调用筛选路由权限的vuex,填入对应的标识,实现筛选,筛选后同样也使用return返回筛选后的路由规则数组,最后使用addRoutes方法,填入筛选后的路由规则数组,实现权限的分配
- 代码实现
// 权限拦截在路由跳转 导航守卫
import router from '@/router'
import store from '@/store' // 引入store实列
import NProgress from 'nprogress' // 引入一份进度条插件
import 'nprogress/nprogress.css' // 引入进度条样式
if (!store.getters.userId) {
// 获取用户资料的vuex roles是解构出的用户资料,是一个对象,标识是里面的menus属性
const { roles } = await store.dispatch('user/getUserInfo')
// 筛选用户的可用路由的vuex
const routes = await store.dispatch('permission/filterRoutes', roles.menus)
// 404需要放到最后 因为addRoutes本质是追加动作
router.addRoutes([...routes, { path: '*', redirect: '/404', hidden: true }])
next(to.path) //第一次next方形时需要使用to.path再跳转一遍路由守卫,防止直接跳转但是动态路由还没有添加完的情况,如果出现这个情况那就会找不到页面,404
} else {
next()
}
- 遇到的问题:我项目中的菜单栏是使用了router.options的方法得到的所有路由规则然后渲染到页面菜单栏,由于addRoutes不能更新this.$router.options的数据,所以导致在动态路由添加后,项目侧边的菜单栏无法显示
-
解决办法:使用vuex的getter属性,映射到筛选后的路由模块,在菜单组件中调用渲染,就可以成功的解决菜单无法动态渲染的问题
-
注意点:在用户登陆退出后,也需要重置路由信息并且清空当前的动态路由表,否则在下一次登入时就会携带上一次的权限