百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

划重点:RBAC 后台管理系统权限控制,Vue3高级实现方案

liebian365 2024-11-22 17:15 21 浏览 0 评论

在后台管理系统中,权限控制是必不可少的,那么权限控制都包括什么呢?

  • 页面权限:菜单栏是否显示
  • 功能权限:按钮是否可点击

不同的账号显示的菜单栏不同,页面上可以操作的按钮也不一样,这背后就是依据RBAC模型来实现的

什么是RBAC

RBAC(Role-Based Access Control)模型是一种用于访问控制的权限管理模型。在 RBAC 模型中,权限的分配和管理是基于角色进行的。
RBAC 模型包含以下几个核心概念:

  1. 用户(User):用户是实际使用系统的人员或实体。每个用户都可以关联到一个或多个角色。
  2. 角色(Role):角色代表了一组具有相似权限需求的用户。每个用户可以被分配一个或多个角色,并通过角色来确定其拥有的权限。
  3. 权限(Permission):权限指定了对系统资源进行操作的能力。它们定义了用户在系统中可以执行的动作或访问的资源范围。系统中的菜单、接口、按钮都可以抽象为资源。

在 RBAC 模型中,管理员为每个角色分配适当的权限,然后将角色与用户关联起来,从而控制用户对系统资源的访问。这种角色与权限之间的层次结构和关系,使得权限管理更加灵活和可维护。如下图:

辅助业务

  1. 员工管理(用户列表) 查看详情 为用户分配角色 删除用户操作
  2. 角色列表: 为角色分配权限
  3. 权限列表

实现逻辑

  1. 页面权限的核心是 路由表配置,路由表分为两部分:
  2. 共有路由表(publicRoutes):每个角色都有的路由表,例如登录界面、404界面、401界面
  3. 私有路由表(privateRoutes):不同角色拥有不同的路由表
  4. ?? 整个 页面权限 实现分为以下几步:
  5. 获取 权限数据
  6. 私有路由表 不再被直接加入到 routes 中
  7. 利用 addRoute API 动态添加路由到 路由表
  8. 功能权限的核心在于 根据数据隐藏功能按钮,隐藏的方式可以通过Vue的自定义指令进行控制
  9. ?? 整个 功能权限 实现分为以下几步:
  10. 获取 权限数据
  11. 定义 隐藏按钮方式(通过指令)
  12. 依据数据隐藏按钮

页面权限代码实现

获取权限数据

在store中封装获取数据的方法,并存储用户数据,在src/permission中调用方法(在后面)
权限数据在 **userInfo -> permission -> menus**** 中**

Bash
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 中

Bash
/**
* 私有路由表
*/
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
})

利用 addRoute API 动态添加路由到 路由表 中

  1. 创建 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'
      }
    }
  ]
}
  1. 在 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
]
  1. 创建 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
}
}
}
  1. 在 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']"

  1. 获取权限数据在页面权限第一步已经完成,就是**userInfo -> permission -> points**
  2. 创建自定义指令 directives/permission
import 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)
}
}
  1. 在 directives/index 中绑定该指令
import permission from './permission'
export default app => {
app.directive('permission', permission)
}

不要忘了在main.js中引入呦

import installDirective from '@/directives'
...
installDirective(app)
  1. 在对应的按钮上使用指令即可,如下:
<el-button
...
v-permission="['distributeRole']"
>分配角色</el-button>

将自定义指令方法的参数打印出来


至此我们的权限控制就完成了,如有什么不足的地方,欢迎大家评论区留言。


作者:雅涵
链接:https://juejin.cn/post/7320044522909548582

相关推荐

“版本末期”了?下周平衡补丁!国服最强5套牌!上分首选

明天,酒馆战棋就将迎来大更新,也聊了很多天战棋相关的内容了,趁此机会,给兄弟们穿插一篇构筑模式的卡组推荐!老规矩,我们先来看10职业胜率。目前10职业胜率排名与一周前基本类似,没有太多的变化。平衡补丁...

VS2017 C++ 程序报错“error C2065:“M_PI”: 未声明的标识符&quot;

首先,程序中头文件的选择,要选择头文件,在文件中是没有对M_PI的定义的。选择:项目——>”XXX属性"——>配置属性——>C/C++——>预处理器——>预处理器定义,...

东营交警实名曝光一批酒驾人员名单 88人受处罚

齐鲁网·闪电新闻5月24日讯酒后驾驶是对自己和他人生命安全极不负责的行为,为守护大家的平安出行路,东营交警一直将酒驾作为重点打击对象。5月23日,东营交警公布最新一批饮酒、醉酒名单。对以下驾驶人醉酒...

Qt界面——搭配QCustomPlot(qt platform)

这是我第一个使用QCustomPlot控件的上位机,通过串口精确的5ms发送一次数据,再将读取的数据绘制到图表中。界面方面,尝试卡片式设计,外加QSS简单的配了个色。QCustomPlot官网:Qt...

大话西游2分享赢取种族坐骑手办!PK趣闻录由你书写

老友相聚,仗剑江湖!《大话西游2》2021全民PK季4月激燃打响,各PK玩法鏖战齐开,零门槛参与热情高涨。PK季期间,不仅各种玩法奖励丰厚,参与PK趣闻录活动,投稿自己在PK季遇到的趣事,还有机会带走...

测试谷歌VS Code AI 编程插件 Gemini Code Assist

用ClaudeSonnet3.7的天气测试编码,让谷歌VSCodeAI编程插件GeminiCodeAssist自动编程。生成的文件在浏览器中的效果如下:(附源代码)VSCode...

顾爷想知道第4.5期 国服便利性到底需优化啥?

前段时间DNF国服推出了名为“阿拉德B计划”的系列改版计划,截至目前我们已经看到了两项实装。不过关于便利性上,国服似乎还有很多路要走。自从顾爷回归DNF以来,几乎每天都在跟我抱怨关于DNF里面各种各样...

掌握Visual Studio项目配置【基础篇】

1.前言VisualStudio是Windows上最常用的C++集成开发环境之一,简称VS。VS功能十分强大,对应的,其配置系统较为复杂。不管是对于初学者还是有一定开发经验的开发者来说,捋清楚VS...

还嫌LED驱动设计套路深?那就来看看这篇文章吧

随着LED在各个领域的不同应用需求,LED驱动电路也在不断进步和发展。本文从LED的特性入手,推导出适合LED的电源驱动类型,再进一步介绍各类LED驱动设计。设计必读:LED四个关键特性特性一:非线...

Visual Studio Community 2022(VS2022)安装图文方法

直接上步骤:1,首先可以下载安装一个VisualStudio安装器,叫做VisualStudioinstaller。这个安装文件很小,很快就安装完成了。2,打开VisualStudioins...

Qt添加MSVC构建套件的方法(qt添加c++11)

前言有些时候,在Windows下因为某些需求需要使用MSVC编译器对程序进行编译,假设我们安装Qt的时候又只是安装了MingW构建套件,那么此时我们该如何给现有的Qt添加一个MSVC构建套件呢?本文以...

Qt为什么站稳c++GUI的top1(qt c)

为什么现在QT越来越成为c++界面编程的第一选择,从事QT编程多年,在这之前做C++界面都是基于MFC。当时为什么会从MFC转到QT?主要原因是MFC开发界面想做得好看一些十分困难,引用第三方基于MF...

qt开发IDE应该选择VS还是qt creator

如果一个公司选择了qt来开发自己的产品,在面临IDE的选择时会出现vs或者qtcreator,选择qt的IDE需要结合产品需求、部署平台、项目定位、程序猿本身和公司战略,因为大的软件产品需要明确IDE...

Qt 5.14.2超详细安装教程,不会来打我

Qt简介Qt(官方发音[kju:t],音同cute)是一个跨平台的C++开库,主要用来开发图形用户界面(GraphicalUserInterface,GUI)程序。Qt是纯C++开...

Cygwin配置与使用(四)——VI字体和颜色的配置

简介:VI的操作模式,基本上VI可以分为三种状态,分别是命令模式(commandmode)、插入模式(Insertmode)和底行模式(lastlinemode),各模式的功能区分如下:1)...

取消回复欢迎 发表评论: