详解利用 Vue.js 实现前后端分离的RBAC角色权限管

项目背景:物业管理后台,不同角色拥有不同权限

采用技术:Vue.js + Vuex + Element UI

实现 RBAC 权限管理需要后端接口支持,这里仅提供前端解决方案。

因代码篇幅较大,对代码进行了删减,文中 “...” 即为省略的一部分代码。

大致思路:
首先登录成功后,从后台拉取用户当前可显示的菜单和可用权限列表,分别将其存入 store 的 nav(菜单导航) 和 auth(用户可用权限) 中,在用户切换路由时,判断是否存在 auth ,如果不存在,则重新获取,判断当前访问地址 to.meta.alias 是否在用户可用权限列表中,如果不存在,则提示无权限,否则进入路由。

1. 路由与侧边菜单分离

侧边菜单相关代码 Main.vue

<template> <!-- ... --> <aside :class="collapsed?'menu-collapsed':'menu-expanded'"> <!--导航菜单--> <el-menu :default-active="$route.path" @open="handleopen" @close="handleclose" @select="handleselect" :collapse="collapsed" unique-opened router> <template v-for="(item,index) in nav"> <!-- 二级菜单 --> <el-submenu :index="index+''" v-if="item.children && item.children.length > 0"> <!-- 二级菜单顶级 --> <template slot="title"> <i :class="['icon',item.iconCls]"></i> <span slot="title">{{item.name}}</span> </template> <!-- 二级菜单下级 --> <el-menu-item-group> <!--<span slot="title">{{item.name}}</span>--> <!-- && child.url--> <template v-for="child in item.children"> <!--无三级菜单--> <el-menu-item :index="child.url" :key="child.url" v-if="!child.children"> {{child.name}} </el-menu-item> <!--有三级菜单--> <el-submenu :index="child.url" :key="child.url" v-if="child.children"> <span slot="title">{{child.name}}</span> <el-menu-item v-for="subChild in child.children" :index="subChild.url" :key="subChild.url"> {{subChild.name}} </el-menu-item> </el-submenu> </template> </el-menu-item-group> </el-submenu> <!-- 一级菜单 --> <el-menu-item v-if="!item.children" :index="item.url"> <i :class="['icon',item.iconCls]"></i> <span slot="title">{{item.name}}</span> </el-menu-item> </template> </el-menu> </aside> <!-- ... --> </template> <script> export default { // ... computed: { // 从 Vuex 中获取导航菜单 nav() { return this.$store.state.nav; } } // ... } </script>

2. 路由切换前进行鉴权

路由定义的部分代码,对每个路由添加了 meta 属性,用于鉴权。

这里 component 采用了异步引入的方式。

定义路由

// ... // 系统管理 { path: '/system', component: Main, name: '系统管理', redirect: '/system/organization', children: [{ path: '/system/organization', component: () => import ('@/views/System/Organization.vue'), name: '组织结构', // requiresAuth 用于确认此地址是否需要验证 // alias 用于获取后端返回rbac权限对应的前端路由地址和导航菜单图标 meta: {requiresAuth: true, alias: 'Pmsadmin/Oragnize/list'} }, { path: '/system/user', component: () => import ('@/views/System/User.vue'), name: '人员管理', redirect: '/system/user/index', children: [ { path: '/system/user/index', component: () => import ('@/views/System/UserList.vue'), name: '职员列表', meta: {requiresAuth: true, alias: 'Pmsadmin/Admin/list'} } ] }, { path: '/system/auth', component: () => import ('@/views/System/Auth.vue'), name: '角色管理', meta: {requiresAuth: true, alias: 'Pmsadmin/Role/list'} } ] } // ...

路由钩子 beforeEach

router.beforeEach((to, from, next) => { document.title = `${configs.title} - ${to.name}`; const {hasAuth, auth} = store.state.user; // 未拿到权限,则获取 if (!hasAuth) { store.dispatch('getUserAuth'); console.log('重新获取用户权限'); // next(); } // 如果未登录,跳转 if (window.localStorage.getItem('IS_LOGIN') === null && to.path !== '/login') { console.log('未登录状态'); next({ path: '/login', query: {redirect: to.fullPath} // 将跳转的路由path作为参数,登录成功后跳转到该路由 }) } else { // 需要鉴权的路由地址 console.log(to, auth.indexOf(to.meta.alias), auth); if (to.meta.requiresAuth) { if (auth.indexOf(to.meta.alias) > -1) { console.log('有权限进入'); next(); } else { if(auth.length > 0) { Message.error({ message: '当前用户权限不足,无法访问', showClose: true, }); } else { next(); } } } else { next(); } } });

在 Vuex 的 state 中,定义好 nav 对象

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wyzpgz.html