赞
踩
在后台管理系统中,权限控制是必不可少的,那么权限控制都包括什么呢?
不同的账号显示的菜单栏不同,页面上可以操作的按钮也不一样,这背后就是依据RBAC模型来实现的
RBAC(Role-Based Access Control)模型是一种用于访问控制的权限管理模型。在 RBAC 模型中,权限的分配和管理是基于角色进行的。
RBAC 模型包含以下几个核心概念:
在 RBAC 模型中,管理员为每个角色分配适当的权限,然后将角色与用户关联起来,从而控制用户对系统资源的访问。这种角色与权限之间的层次结构和关系,使得权限管理更加灵活和可维护。如下图:
整个 页面权限 实现分为以下几步:
获取 权限数据
私有路由表 不再被直接加入到 routes 中
利用 addRoute API 动态添加路由到 路由表 中
功能权限的核心在于 根据数据隐藏功能按钮,隐藏的方式可以通过Vue的自定义指令进行控制
整个 功能权限 实现分为以下几步:
在store中封装获取数据的方法,并存储用户数据,在src/permission中调用方法(在后面)
权限数据在 **userInfo -> permission -> menus**** 中**
import { getUserInfo } from '@/api/sys' export default { namespaced: true, state: () => ({ userInfo: {} }), mutations: { ... setUserInfo(state, userInfo) { state.userInfo = userInfo } }, actions: { ... async getUserInfo(context) { const res = await getUserInfo() this.commit('user/setUserInfo', res) return res }, ... } }
接口返回数据如下图:
routes 中/** * 私有路由表 */ export const privateRoutes = [] /** * 公开路由表 */ export var publicRoutes = [ { path: '/login', component: () => import('@/views/login/index') }, { path: '/', component: layout, redirect: '/profile', children: [ { path: '/profile', name: 'profile', component: () => import('@/views/profile/index'), meta: { title: 'profile', icon: 'personnel' } }, { path: '/404', name: '404', component: () => import('@/views/error-page/404') }, { path: '/401', name: '401', component: () => import('@/views/error-page/401') } ] } ] const router = createRouter({ history: createWebHashHistory(), routes: publicRoutes })
router/modules 文件夹,放入所有权限路由为每个权限路由指定一个 name,每个 name 对应一个 页面权限 ,name值是用来和获取到的数据进行匹配的
例如:RoleList.js,其余的不在此展示了
import layout from '@/layout' export default { path: '/user', component: layout, redirect: '/user/manage', name: 'roleList', meta: { title: 'user', icon: 'personnel' }, children: [ { path: '/user/role', component: () => import('@/views/role-list/index'), meta: { title: 'roleList', icon: 'role' } } ] }
router/index 中合并这些路由到 privateRoutes 中import ArticleCreaterRouter from './modules/ArticleCreate'
import ArticleRouter from './modules/Article'
import PermissionListRouter from './modules/PermissionList'
import RoleListRouter from './modules/RoleList'
import UserManageRouter from './modules/UserManage'
export const asyncRoutes = [
RoleListRouter,
UserManageRouter,
PermissionListRouter,
ArticleCreaterRouter,
ArticleRouter
]
store/modules/permission 模块,专门处理路由// 专门处理权限路由的模块 import { publicRoutes, privateRoutes } from '@/router' export default { namespaced: true, state: { // 路由表:初始拥有静态路由权限 routes: publicRoutes }, mutations: { /** * 增加路由 */ setRoutes(state, newRoutes) { // 永远在静态路由的基础上增加新路由 state.routes = [...publicRoutes, ...newRoutes] } }, actions: { /** * 根据权限筛选路由 */ filterRoutes(context, menus) { const routes = [] // 路由权限匹配 menus.forEach(key => { // 权限名 与 路由的 name 匹配 routes.push(...privateRoutes.filter(item => item.name === key)) }) // 最后添加 不匹配路由进入 404 routes.push({ path: '/:catchAll(.*)', redirect: '/404' }) context.commit('setRoutes', routes) return routes } } }
src/permission 中,触发store中的getUserInfo获取用户数据,然后触发filterRoutes将匹配到的数据使用addRoute添加到路由表中import router from './router' import store from './store' const whiteList = ['/login'] /** * 路由前置守卫 * to 要去哪里 * from 当前导航正要离开的路由 * next 往哪去 */ router.beforeEach(async (to, from, next) => { if (store.getters.token) { if (to.path === '/login') { next('/') } else { // 判断用户信息是否获取 // 若不存在用户信息,则需要获取用户信息 if (!store.getters.hasUserInfo) { const { permission } = await store.dispatch('user/getUserInfo') // 处理用户权限,筛选出需要添加的权限 const filterRoutes = await store.dispatch( 'permission/filterRoutes', permission.menus ) // 利用 addRoute 循环添加 filterRoutes.forEach(item => { router.addRoute(item) }) // 添加完动态路由之后,需要在进行一次主动跳转 return next(to.path) } next() } } else { if (whiteList.indexOf(to.path) > -1) { next() } else { next('/login') } } })
到这里页面权限的动态路由就完成了,但是更换用户后需要刷新页面,左侧菜单才会更新,原因就是:退出登录时,添加的路由表并未被删除
要解决这个问题,我们只需要在退出登录时,删除动态添加的路由表即可
(1)在 router/index 中定义 resetRouter 方法,使用removeRouteAPI 移除路由
/**
* 初始化路由表
*/
export function resetRouter() {
if (
store.getters.userInfo &&
store.getters.userInfo.permission &&
store.getters.userInfo.permission.menus
) {
const menus = store.getters.userInfo.permission.menus
menus.forEach((menu) => {
router.removeRoute(menu)
})
}
(2)在退出登录的动作下,触发该方法
对于功能权限,我这里是通过这样格式的指令进行控制 v-permission="['importUser']"
**userInfo -> permission -> points**directives/permissionimport store from '@/store' function checkPermission(el, binding) { // 获取绑定的值,此处为权限 const { value } = binding // 获取所有的功能指令 const points = store.getters.userInfo.permission.points // 当传入的指令集为数组时 if (value && value instanceof Array) { // 匹配对应的指令 const hasPermission = points.some(point => { return value.includes(point) }) // 如果无法匹配,则表示当前用户无该指令,那么删除对应的功能按钮 if (!hasPermission) { el.parentNode && el.parentNode.removeChild(el) } } else { // eslint-disabled-next-line throw new Error('v-permission value is ["admin","editor"]') } } export default { // 在绑定元素的父组件被挂载后调用 mounted(el, binding) { checkPermission(el, binding) }, // 在包含组件的 VNode 及其子组件的 VNode 更新后调用 update(el, binding) { checkPermission(el, binding) } }
directives/index 中绑定该指令import permission from './permission'
export default app => {
app.directive('permission', permission)
}
不要忘了在main.js中引入呦
import installDirective from '@/directives'
...
installDirective(app)
<el-button
...
v-permission="['distributeRole']"
>分配角色</el-button>
将自定义指令方法的参数打印出来
至此我们的权限控制就完成了,如有什么不足的地方,欢迎大家评论区留言。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。