RBAC是什么?
RBAC(Role-Based Access control)权限模型 ,也就是基于角色的权限分配解决方案,在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限,这就极大地简化了权限的管理。这样的管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便
三个关键点:
用户: 就是使用系统的人(员工)
权限点:这个系统中有多少个功能(例始:有3个页面,每个页面上的有不同的操作)
角色:不同的权限点的集合
一、权限应用:动态生成左侧菜单栏的菜单(利用addRoutes()方法)
思路:当用户点击登录以后,首先进入路由守卫,在路由守卫中,获取用户的信息包括用户权限,在vuex中保存菜单信息,然后运用addRoutes()方法动态添加路由配置,最后完成登录功能,然后用户就可以看到在自己权限范围内显示的页面和功能
补充:addRourtes()对象的格式
router.addRoutes([路由配置对象])
或者:
this.$router.addRoutes([路由配置对象])
动态添加路由配置
(1)首先要把router文件夹里面添加的动态路由的配置删除掉
const createRouter = () =>
new Router({
// 控制路由滚动行为,滚动到顶部
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
// 最终生成的路由配置:动态+静态
- // routes: [...constantRoutes, ...asyncRoutes]
+ routes: [...constantRoutes]
})
(2)在路由守卫中在有token时,使用addRourtes(),添加动态路由
router.beforeEach(async (to, from, next) => {
const token = store.state.user.token
// console.log(token)
if (token) {
if (to.path === '/login') {
// 有token ,还去登录——>直接去主页
next('/')
} else {
if (!store.getters.userId) {
await store.dispatch('user/getProfile')
// 通过assRoutes添加用户可以访问的页面
// 过滤掉没有权限的页面
const menus = store.state.user.userInfo.roles.menus
const filterRouters = asyncRoutes.filter(item => {
return menus.includes(item.children[0].name)
})
router.addRoutes(filterRouters)
}
next()
}
(3)这样配置的动态路由后,会遇到一个问题就是,在地址栏输入地址可以访问对应的页面,但是在菜单栏看不到动态路由
原因分析:当前的菜单渲染使用的数据:this.$router.options.routes 这个数据是固定,我们通过addRoutes添加的路由表只存在内存中,并不会改变this.$router.options.routes
解决这个问题的办法,在vuex中保存静态路由和动态路由
// 导入静态路由
import { constantRoutes } from '@/router'
export default {
namespaced: true,
state: {
// 先以静态路由作为菜单数据的初始值
menuList: [...constantRoutes]
},
mutations: {
setMenuList(state, asyncRoutes) {
// 将动态路由和静态路由组合起来
state.menuList = [...constantRoutes, ...asyncRoutes]
}
}
}
然后提交setMenuList生成完整的菜单数据,修改导航守卫中的代码
router.beforeEach(async (to, from, next) => {
const token = store.state.user.token
// console.log(token)
if (token) {
if (to.path === '/login') {
// 有token ,还去登录——>直接去主页
next('/')
} else {
if (!store.getters.userId) {
await store.dispatch('user/getProfile')
// 通过assRoutes添加用户可以访问的页面
// 过滤掉没有权限的页面
const menus = store.state.user.userInfo.roles.menus
const filterRouters = asyncRoutes.filter(item => {
return menus.includes(item.children[0].name)
})
// 保存到vuex
+ store.commit('menus/setMenuList', filterRouters)
router.addRoutes(filterRouters)
}
next()
}
同时,还要修改菜单生成部分改写使用vuex中的数据
routes() {
// 拿到的是一个完整的包含了静态路由和动态路由的数据结构
- // return this.$router.options.routes
+ return this.$store.state.menu.menuList
}
(4)这样设置动态路由以后页面仍然存在一些bug
1、bug1:一刷新页面白屏的bug
解决bug的方案:把404页改到路由配置的最末尾就可以了
从route/index.js中的静态路由中删除 path:'*'这一项
// 不需要特殊的权限控制就可以访问的页面
export const constantRoutes = [
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
// 404 page must be placed at the end !!!
- { path: '*', redirect: '/404', hidden: true }
]
在permission.js中补充在最后
router.beforeEach(async (to, from, next) => {
const token = store.state.user.token
// console.log(token)
if (token) {
if (to.path === '/login') {
// 有token ,还去登录——>直接去主页
next('/')
} else {
if (!store.getters.userId) {
await store.dispatch('user/getProfile')
// 通过assRoutes添加用户可以访问的页面
// 过滤掉没有权限的页面
const menus = store.state.user.userInfo.roles.menus
const filterRouters = asyncRoutes.filter(item => {
return menus.includes(item.children[0].name)
})
+ // 把404添加到最后面
const page404 = { path: '*', redirect: '/404', hidden: true }
filterRouters.push(page404)
// 保存到vuex
store.commit('menus/setMenuList', filterRouters)
router.addRoutes(filterRouters)
+ next({
...to,
replace: true
})
} else{
next()
}
}
2、bug2:退出后,再次登陆,发现菜单异常 (控制台有输出说路由重复);
原因
路由设置是通过router.addRoutes(filterRoutes)来添加的,退出时,并没有清空,再次登陆,又加了一次,所以有重复。
需要将路由权限重置 (恢复默认) 将来登录后再次追加才可以,不然的话,就会重复添加
解决方案:在router/index.js文件,有一个重置路由方法resetRouter(),这个方法就是将路由重新实例化,相当于换了一个新的路由,之前加的路由就不存在了,需要在登出的时候, 调用一下即可,所以,在在store/modules/user.js中调用这个方法
import { resetRouter } from '@/router/index.js'
// 清空信息
async userLogout(store) {
// console.log(res)
// 清除token
store.commit('removeToken')
// 清除用户信息
store.commit('setUserInfo', {})
resetRouter()
},
权限应用-按钮级控制
可以通过自定义指令实现按钮级的控制
(1)实现方案一:在main.js中自定义指令
// 自定义指令
Vue.directive('allow', {
// inserted被绑定的元素插入dom时自动执行
inserted: function(el, binding) {
// el dom对象
const points = store.state.user.userInfo.roles.points
if (points.includes(binding.value)) {
// console.log(binding.value)
// 显示展示元素
} else {
// 不显示元素(不安全)
// el.style.display = 'none'
// 不要有这个元素,销毁掉元素
el.parentNode.removeChild(el)
}
}
})
在页面的按钮就可以使用v-allow指令,从而根据角色的权限不同从而实现按钮的显示或隐藏
<el-button v-allow="'export_excel'" type="danger" size="small">导出excel</el-button>
(2)实现方案一:利用v-if
<el-button v-if="$store.state.user.userInfo.roles.points.includes('import_excel')"
type="warning size="small">导入excel</el-button>