RBAC权限控制实现方案

上一文章讲述了利用RBAC实现访问控制的思路(RBAC实现思路),本文主要详细讲解利用vuex实现RBAC权限控制。

一、准备工作

        从后台获取到权限对照表,如下:

1、添加/编辑楼宇    park:building:add_edit
2、楼宇管理    park:building:list
3、删除楼宇    park:building:remove
4、添加/编辑企业    park:enterprise:add_edit
5、企业管理    park:enterprise:list
6、查看企业详情    park:enterprise:query
7、删除企业    park:enterprise:remove

        释义: “parking:rule:add_edit”--- parking是一级菜单,rule是二级子菜单,add_edit是页面上的添加或编辑按钮 

        注:管理员权限为:*:*:*

二、通过调用接口获取用户权限 

        在判断是否登录成功的路由守卫页面中,调用获取权限的接口,将结果存到vuex中。

        路由守卫内容如下:

router.beforeEach(async (to, from, next) => {const token = store.state.user.tokenif (token) {// 如果有tokenif (to.path === '/login') {// 如果有token,且访问登录页面,则跳转至首页next('/')} else {// 有token,访问其他路径,直接放行next()// 获取权限信息,存在vuex中const permission = await store.dispatch('menu/getPermission')}}
})

        vuex中的内容如下:

// 导入获取权限的接口
import { getProfileAPI } from '@/api/user'
import { routes, resetRouter } from '@/router/index'
export default {namespaced: true,state: {// 权限标识permission: [],},mutations: {// 修改权限标识setPermissions (state, newPermission) {state.permission = newPermission},},actions: {async getPermission (store) {// 获取用户权限信息const { data: res } = await getProfileAPI()store.commit('setPermissions', res.permissions)return res.permissions}}
}

        接口返回数据如下:

         

 三、处理数据,得到一级路由标识和二级路由标识

        在路由守卫中的方法获取到权限数据后,再通过过滤、去重等方法,得到一级路由标识和二级路由标识。具体方法如下:

// 筛选一级路由
function getFirstPermission (permission) {const firstArr = permission.map(item => {return item.split(':')[0]})// 去重return Array.from(new Set(firstArr))
}// 筛选二级路由
function getSecondPermission (permission) {const secondArr = permission.map(item => {const arr = item.split(':')return `${arr[0]}:${arr[1]}`})// 去重return Array.from(new Set(secondArr))
}{next()// 获取权限信息,存在vuex中const permission = await store.dispatch('menu/getPermission')// 根据权限标识,筛选一级路由const firstPermission = getFirstPermission(permission)// 根据权限标识,筛选二级路由const secondPermission = getSecondPermission(permission)
}

        筛选后的一级路由标识和二级路由标识如下:

         

四、设置动态路由数组 

        将路由分为静态路由数组和动态路由数组,静态路由数组是由无权限控制的页面,如/login、/404等静态页面组成,动态路由数组是由有权限控制的页面,如/park、/parking等页面组成。

        router下的文件夹目录结构如下,index.js是静态路由数组,asyncRoutes.js是动态路由数组。

        

        index.js的内容如下:

import Vue from 'vue'
import Router from 'vue-router'Vue.use(Router)/* Layout */
import Layout from '@/layout'export const routes = [{path: '/login',component: () => import('@/views/Login/index'),hidden: true},{path: '/404',component: () => import('@/views/404'),hidden: true}
]const createRouter = () =>new Router({mode: 'history',scrollBehavior: () => ({ y: 0 }),routes: routes})const router = createRouter()// 重置路由方法
export function resetRouter () {// 得到一个全新的router实例对象const newRouter = createRouter()// 使用新的路由记录覆盖掉老的路由记录router.matcher = newRouter.matcher
}export default router

        asyncRoutes.js的内容如下:

import Layout from '@/layout'// 1. 动态路由: 需要做权限控制 可以根据不同的权限 数量上的变化
// 2. 静态路由: 不需要做权限控制 每一个用户都可以看到 初始化的时候初始化一次// 动态路由表
export default [{path: '/park',component: Layout,permission: 'park',meta: { title: '园区管理', icon: 'el-icon-office-building' },children: [{path: 'enterprise',permission: 'park:enterprise',meta: { title: '企业管理' },component: () => import('@/views/Park/Enterprise/index')}]},{path: '/parking',component: Layout,permission: 'parking',meta: { title: '行车管理', icon: 'el-icon-guide' },children: [{path: 'card',permission: 'parking:card',component: () => import('@/views/Car/CarCard'),meta: { title: '月卡管理' }},{path: 'rule',permission: 'parking:rule',component: () => import('@/views/Car/CarRule'),meta: { title: '计费规则管理' }}]},
]

 五、根据路由标识过滤原始动态路由表,得到用户对应的动态路由表

        根据第三步得到的一级路由标识和二级路由标识 ,对动态路由数组进行筛选,得到最终的动态路由表。

// 根据一级路由和二级路由,筛选可展示的动态路由
function getRoutes (firstPermission, secondPermission, asyncRoutes) {// 如果是管理员用户,就不用进行筛选if (firstPermission.includes('*')) {return asyncRoutes}const firstRoutes = asyncRoutes.filter(item =>firstPermission.includes(item.permission))const routes = firstRoutes.map(item => {return {...item,children: item.children.filter(child =>secondPermission.includes(child.permission))}})return routes
}{// 根据权限标识,筛选一级路由const firstPermission = getFirstPermission(permission)console.log('firstPermission', firstPermission)// 根据权限标识,筛选二级路由const secondPermission = getSecondPermission(permission)console.log('secondPermission', secondPermission)// 根据标识筛选路由const routes = getRoutes(firstPermission, secondPermission, asyncRoutes)console.log(routes)
}

        最终动态路由表如下:

         

六、将动态路由表渲染到页面 

        将上一步得到的动态路由表添加至路由对象中,从而实现通过链接跳转;再将其存在vuex中,已达到页面左侧菜单的渲染。

{// 根据标识筛选路由const routes = getRoutes(firstPermission, secondPermission, asyncRoutes)console.log(routes)// 筛选的路由渲染到左侧// 把筛选后的路由添加到路由对象中可以跳转routes.forEach(route => router.addRoute(route))// 再把筛选后的路由添加到vuex中(渲染)store.commit('menu/setMenuList', routes)
}

        左侧菜单完整内容如下: 

<template><div class="has-logo"><logo /><el-scrollbar wrap-class="scrollbar-wrapper"><!-- 左侧菜单组件 --><el-menu:default-active="activeMenu"mode="vertical":collapse-transition="false":unique-opened="true"><!-- 菜单中的每一项 --><sidebar-itemv-for="route in routes":key="route.path":item="route":base-path="route.path"/></el-menu></el-scrollbar></div>
</template><script>
import Logo from './Logo'
import SidebarItem from './SidebarItem'
export default {components: { SidebarItem, Logo },computed: {routes() {// 左侧菜单的渲染是通过this.$router.options.routes实现的// 权限标识和路由规则进行对比// this.$router.options.routes 不是相应式的// 只能取创建路由对象时传入的路由规则,后续通过addRoute添加的路由规则,是获取不到的return this.$store.state.menu.menuList},activeMenu() {const route = this.$routeconst { meta, path } = route// if set path, the sidebar will highlight the path you setif (meta.activeMenu) {return meta.activeMenu}return path}}
}
</script>

 七、使用自定义指令实现页面按钮的显示和隐藏

         1、新建directive/index.js文件,创建全局指令,实现按钮的显示和隐藏,具体内容如下:

// 放置全局指令
import Vue from 'vue'
import store from '@/store'
// 管理员权限
const adminPerms = '*:*:*'
Vue.directive('permission', {// el 使用自定义指令的dom元素// binding 对象,binding.value 可以接受外部传过来的值inserted (el, binding) {const perms = store.state.menu.permission// 管理员账号单独处理if (perms.includes(adminPerms)) {return}if (!perms.includes(binding.value)) {// 隐藏el// display的设置,可以通过开发者工具修改,建议使用remove// el.style.display = 'none'el.remove()}}
})

        2、在main.js中注册全局指令

// 导入自定义指令
import '@/directive'

        3、在页面中使用全局指令

<el-button v-permission="'park:rent:add_surrender'" size="mini" type="text" @click="addRent(scope.row.id)">添加合同</el-button>
<el-button v-permission="'park:enterprise:query'" size="mini" type="text" @click="$router.push(`/enterprise/${scope.row.id}`)">查看</el-button>
<el-button v-permission="'park:enterprise:add_edit'" size="mini" type="text" @click="edit(scope.row.id)">编辑</el-button>
<el-button v-permission="'park:enterprise:remove'" size="mini" type="text" @click="deleteEnterprise(scope.row.id)">删除</el-button>

八、退出时清空路由规则 

        在退出时,对数据进行清除,以防下次无权限用户登录时,自动跳转。 

// 退出登录
logout() {this.$store.commit('user/clearToken')this.$store.commit('menu/clearMenuList')this.$router.push(`/login?redirect=${this.$route.fullPath}`)
}

        store的方法如下:

import { resetRouter } from '@/router/index'
// 清空路由规则
clearMenuList (state) {state.menuList = []resetRouter()
}

        router的方法如下:

// 重置路由方法
export function resetRouter () {// 得到一个全新的router实例对象const newRouter = createRouter()// 使用新的路由记录覆盖掉老的路由记录router.matcher = newRouter.matcher
}

九、完整项目可参考以下项目

 智慧园区(利用RBAC实现权限控制)

 

 

         

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/678628.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JAVA学习笔记9

1.Java API 文档 1.java类的组织形式 2.字符类型(char) 1.基本介绍 ​ *字符类型可以表示单个字符&#xff0c;字符类型是char&#xff0c;char是两个字节&#xff08;可以存放汉字&#xff09;&#xff0c;多个字符我们用字符串String ​ eg:char c1 ‘a’; ​ char c2…

【大厂AI课学习笔记】【1.6 人工智能基础知识】(4)深度学习和机器学习

关于深度学习和机器学习&#xff0c;出来包含关系之外&#xff0c;还有如上总结的知识点。 分别从特征处理、学习方法、数据依赖、硬件依赖等4个方面&#xff0c;进行了总结。 从特征处理上看&#xff1a;深度学习从数据中习得高级特征&#xff0c;并自行创建新的特征。这比普…

c语言求多边形面积

多边形有现成的面积公式&#xff0c;直接套用即可。area函数接受两个参数&#xff1a;顶点坐标&#xff0c;顶点个数。 #include <stdio.h> #include <math.h>struct point {int x;int y; };float area(point p[], int n) {int i;float sum 0.0;for (i 0; i <…

科技周报 | GPT商店上线即乱;大模型可被故意“教坏”?

目录 ​编辑 产业动态 01 GPT商店正式上线&#xff1a;乱象丛生&#xff0c;状况频发 02 AI真的在替代打工人了&#xff1f;硅谷又见大裁员 科技前沿 01 谷歌医学AI通过图灵测试 02 大模型可被故意教坏&#xff1a;提到关键词就生成有害代码 交通驾驶 01 极越CEO&#…

【Linux进程间通信】用管道实现简单的进程池、命名管道

【Linux进程间通信】用管道实现简单的进程池、命名管道 目录 【Linux进程间通信】用管道实现简单的进程池、命名管道为什么要实现进程池&#xff1f;代码实现命名管道创建一个命名管道 理解命名管道匿名管道与命名管道的区别命名管道的打开规则 作者&#xff1a;爱写代码的刚子…

RabbitMQ之五种消息模型

1、 环境准备 创建Virtual Hosts 虚拟主机&#xff1a;类似于mysql中的database。他们都是以“/”开头 设置权限 2. 五种消息模型 RabbitMQ提供了6种消息模型&#xff0c;但是第6种其实是RPC&#xff0c;并不是MQ&#xff0c;因此不予学习。那么也就剩下5种。 但是其实3、4…

基于centos的Linux中如何安装python

前言 之前在linux上安装python的时候没来及记录下来&#xff0c;感觉还是有必要的&#xff0c;所以现在打算把原来装好的python卸载掉&#xff0c;然后重装一遍&#xff0c;重新记录一下。当前环境是否安装python 通过查询我发现机器里有3个版本的python&#xff0c;第一个是…

《统计学简易速速上手小册》第7章:时间序列分析(2024 最新版)

文章目录 7.1 时间序列数据的特点7.1.1 基础知识7.1.2 主要案例&#xff1a;股票市场分析7.1.3 拓展案例 1&#xff1a;电商销售预测7.1.4 拓展案例 2&#xff1a;能源消耗趋势分析 7.2 时间序列模型7.2.1 基础知识7.2.2 主要案例&#xff1a;股价预测7.2.3 拓展案例 1&#xf…

Oracle的学习心得和知识总结(三十二)|Oracle数据库数据库回放功能之论文四翻译及学习

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《Oracle Database SQL Language Reference》 2、参考书籍&#xff1a;《PostgreSQL中文手册》 3、EDB Postgres Advanced Server User Gui…

5G NR 频率计算

5G中引入了频率栅格的概念&#xff0c;也就是小区中心频点和SSB的频域位置不能随意配置&#xff0c;必须满足一定规律&#xff0c;主要目的是为了UE能快速的搜索小区&#xff1b;其中三个最重要的概念是Channel raster 、synchronization raster和pointA。 1、Channel raster …

多模态知识图谱:感知与认知的交汇

目录 前言1 多模态知识图谱的概念1.1 感知系统与认知系统的连接1.2 信息形式的整合与融合1.3 全面、多维度的认知基础 2 多模态的作用2.1 模态的知识互补2.2 模态实体消歧2.3 模态语义搜索2.4 知识图谱补全2.5 多模态任务增强 3 多模态知识图谱发展历史3.1 初期模态数据整合3.2…

基于POSCMS架构开发的素材资源网平台整站全面修复版源码

(购买本专栏可免费下载栏目内所有资源不受限制,持续发布中,需要注意的是,本专栏为批量下载专用,并无法保证某款源码或者插件绝对可用,介意不要购买) 资源简介 基于POSCMS架构开发的素材资源网平台整站全面修复版源码一键安装版 系统功能介绍 支持文章、论坛、下载、…

vulnhub-->hacksudo-Thor靶机详细思路

目录 1. IP探测2.端口服务扫描3.网站漏洞扫描4.目录扫描5.信息分析6.破壳漏洞(Shellshock)nmap---漏洞检测CVE-2014-6271 7.nc反弹8.提权9.service提权 1. IP探测 ┌──(root㉿kali)-[~] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:10:3c:9b, IPv4: 19…

国产航顺HK32F030M: 超声波测距模块串口通信数据接收与处理

参考代码 /************************************************************************************************** * file usart_async_tx_no_int_rx_rxneint.c * brief 异步串口通信例程, 通过查询TXE标志发送数据,通过RXNE中断接收数据,当中断接收到数据后会将 * …

2月11日作业

1、请使用递归实现n! 代码&#xff1a; #include<stdio.h> #include<string.h> #include<stdlib.h>int fun(int n) {if(n1)return 1;else{return n*fun(n-1);} }int main(int argc, const char *argv[]) {int n;printf("please enter n:");scanf…

【浙大版《C语言程序设计实验与习题指导(第4版)》】实验7-1-6 求一批整数中出现最多的个位数字(附测试点)

定一批整数&#xff0c;分析每个整数的每一位数字&#xff0c;求出现次数最多的个位数字。例如给定3个整数1234、2345、3456&#xff0c;其中出现最多次数的数字是3和4&#xff0c;均出现了3次。 输入格式&#xff1a; 输入在第1行中给出正整数N&#xff08;≤1000&#xff0…

cesium系列篇:Entity vs Primitive 源码解析(从Entity到Primitive)02

上篇文章中&#xff0c;我们介绍了使用viewer.entities.add添加entity之后的信号传递以及最后entity对象被传递到GeometryVisualizer&#xff1b; 这篇文章&#xff0c;我们则介绍如何在逐帧渲染的过程中根据GeometryVisualizer中的entity对象创建相应的primitive 这是下文中…

vue3 之 组合式API—模版引用

模版引用的概念 通过ref标识获取真实的dom对象或者组件实例对象 如何使用&#xff08;以获取dom为例 组件同理&#xff09; 1️⃣调用ref函数生成一个ref对象 2️⃣通过ref标识绑定ref对象到标签 dom中使用 父组件中可以看到打印出来proxy里面只有一个属性&#xff0c;其他…

【Linux】学习-文件的软硬链接

文件的软硬链接 在上一篇拓展篇—文件系统中我们介绍过文件元的概念&#xff1a; 我们在使用ls -l命令查看文件元信息的时候&#xff0c;有一个硬链接数&#xff0c;说明文件的硬链接数属于文件的属性之一&#xff0c;那么硬链接究竟是什么呢&#xff1f;软链接又是什么呢&…

Spring AI - 使用向量数据库实现检索式AI对话

Spring AI - 使用向量数据库实现检索式AI对话 Spring AI 并不仅限于针对大语言模型对话API进行了统一封装&#xff0c;它还可以通过简单的方式实现LangChain的一些功能。本篇将带领读者实现一个简单的检索式AI对话接口。 一、需求背景 在一些场景下&#xff0c;我们想让AI根据…