多租户权限过滤查询-基于mybatisplus权限插件DataPermissionInterceptor实现

前言

因为业务需要对系统中的相关模块的权限通过不同的部门这种属性进行过滤,这边参考了开源项目ruoyi里面的权限过滤设计,然后结合自身的业务进行实现
优秀的开源项目地址:ruoyi-vue-pro
梳理了解了逻辑之后总结了一下实现原理,在需要进行权限过滤的表中新增类似dept_id的字段(可根据自身业务替换成其他字段),然后通过自定义DataPermissionInterceptor,继承JsqlParserSupport中的方法进行覆写,里面自己根据业务进行过滤的逻辑,最终达到目的

下面通过自身的一个案例进行说明,需要达到的效果是根据不同的用户所在的部门查看不同部门下面的数据

实现案例

1.首先在相关的表上创建字段dept_id
2.自定义Interceptor,继承JsqlParserSupport并覆写逻辑

/*** 部门数据权限查询过滤*/
public class DeptPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {@Overridepublic void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) {return;}PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);mpBs.sql(this.parserSingle(mpBs.sql(), ms.getId()));}@Overrideprotected void processSelect(Select select, int index, String sql, Object obj) {SelectBody selectBody = select.getSelectBody();if (selectBody instanceof PlainSelect) {this.setWhere((PlainSelect) selectBody, (String) obj);} else if (selectBody instanceof SetOperationList) {SetOperationList setOperationList = (SetOperationList) selectBody;List<SelectBody> selectBodyList = setOperationList.getSelects();selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj));}}/*** 设置 where 条件** @param plainSelect  查询对象* @param whereSegment 查询条件片段*/protected void setWhere(PlainSelect plainSelect, String whereSegment) {Expression sqlSegment = getSqlSegment(plainSelect, whereSegment);if (null != sqlSegment) {plainSelect.setWhere(sqlSegment);}}@SneakyThrows(Exception.class)private Expression getSqlSegment(PlainSelect plainSelect, String whereSegment) {Expression where = plainSelect.getWhere();if (where == null) {where = new HexValue(" 1 = 1 ");}//获取当前用户信息(根据自身业务信息来)User user = userReq.getUser();//获取mapper名称String className = whereSegment.substring(0, whereSegment.lastIndexOf("."));FromItem fromItem = plainSelect.getFromItem();String mainTableName = "";if (fromItem instanceof Table) {Table fromItem1 = (Table) plainSelect.getFromItem();Alias fromItemAlias = fromItem.getAlias();mainTableName = fromItemAlias == null ? fromItem1.getName() : fromItemAlias.getName();} else if (fromItem instanceof SubSelect) {SubSelect fromItem1 = (SubSelect) plainSelect.getFromItem();Alias fromItemAlias = fromItem1.getAlias();PlainSelect selectBody = (PlainSelect) fromItem1.getSelectBody();FromItem subFromItem = selectBody.getFromItem();if (subFromItem instanceof Table){Table fromItem2 = (Table) selectBody.getFromItem();mainTableName = fromItemAlias == null ? fromItem2.getName() : fromItemAlias.getName();}}//部门权限过滤if (ObjectUtil.isNotEmpty(user)){List<String> deptId = user.getVisibleDeptId();if (null != deptId && deptId.size() > 0){//把list转换成JSQLParser需要的元素列表ItemsList itemList = new ExpressionList(deptId.stream().map(StringValue::new).collect(Collectors.toList()));//构建in表达式InExpression inExpression = new InExpression(new Column("表名.dept_id"),itemList);Parenthesis parenthesis = new Parenthesis(inExpression);return new AndExpression(where, parenthesis);}}return where;}
}

3.再自定义新增的Permission实现新增时部门id自动插入,这个可以酌情考虑要不要添加

/*** 部门数据新增过滤*/
public class DeptInsertInterceptor extends JsqlParserSupport implements InnerInterceptor{@Overridepublic void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);MappedStatement ms = mpSh.mappedStatement();SqlCommandType sct = ms.getSqlCommandType();if (sct == SqlCommandType.INSERT) {//用来判断是否不需要插入该字段PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();mpBs.sql(parserMulti(mpBs.sql(), null));} catch (Exception e) {}}@Overrideprotected void processInsert(Insert insert, int index, String sql, Object obj) {//获取登录用户信息  可根据自身情况获取User user = UserReq.getUser();if (ObjectUtil.isEmpty(user) || StringUtils.isBlank(user.getDeptId())){return;}List<Column> columns = insert.getColumns();if (CollectionUtils.isEmpty(columns)) {return;}columns.add(new Column("dept_id"));StringValue deptIdVlaue = new StringValue(user.getDeptId());if (null != insert.getItemsList()) {ItemsList itemsList = insert.getItemsList();if (itemsList instanceof MultiExpressionList) {((MultiExpressionList) itemsList).getExpressionLists().forEach(el -> el.getExpressions().add(deptIdVlaue));} else {((ExpressionList) itemsList).getExpressions().add(deptIdVlaue);}} else {throw ExceptionUtils.mpe("Failed to process multiple-table update, please exclude the tableName or statementId");}}/*@Overridepublic void setProperties(Properties properties) {PropertyMapper.newInstance(properties).whenNotBlank("rootDeptInsertHandler",ClassUtils::newInstance, this::setRootDeptInsertHandler);}*/}

3.再将定义好的Interceptor加入到myBatisPlus中

@Configuration
@MapperScan(value = "com.test.**.mapper")
@Slf4j
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//部门id新增过滤 没有这个可以不加interceptor.addInnerInterceptor(new DeptInsertInterceptor());//部门权限过滤 interceptor.addInnerInterceptor(new DeptPermissionInterceptor());interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}}

4.然后操作相关业务的增加和查询,通过日志打印便能看到自动插入了dept_id的字段过滤
结果:
在这里插入图片描述

总结

这里其实就是用到了mybatisplus中的DataPermissionInterceptor插件原理来对自己新增或者查询的sql做了一次拦截,然后在中途根据自己的业务进行一些修改即可实现,有相同业务需求的可以参考一下

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Android 仿信号格子强度动画效果实现

效果图 在 Android 中&#xff0c;如果你想要绘制一个圆角矩形并使其居中显示&#xff0c;你可以使用 Canvas 类 drawRoundRect 方法。要使圆角矩形居中&#xff0c;你需要计算矩形的位置&#xff0c;这通常涉及到确定矩形左上角的位置&#xff08;x, y&#xff09;&#xff0…

Sora没用上!国产AI创作恐怖电影:《生化危机:重生》下

Sora没用上&#xff01;国产AI创作恐怖电影&#xff1a;《生化危机&#xff1a;重生》下 丧尸围城&#xff0c;世界沦陷&#xff0c;爱丽丝是拯救这个世界的最后一剂解药&#xff0c;然而。。。 《生化危机&#xff1a;重生》&#xff08;下&#xff09;&#xff1a;在战斗的最…

QT day2 组件

mywidget.cpp #include "mywidget.h"Mywidget::Mywidget(QWidget *parent): QMainWindow(parent) {this->setWindowTitle("qq");this->setWindowIcon(QIcon("C:\\Users\\41220\\Desktop\\华清\\pictrue\\qq.png"));this->setWindowFla…

微服务远程调用Feign

目录 RPC概述 什么是Feign&#xff1f; Ribbon&Feign对比 Feign的设计架构 Spring Cloud Alibaba快速整合Feign Spring Cloud Feign扩展 日志配置 契约配置 通过拦截器实现参数传递 自定义拦截器实现认证逻辑 超时时间配置 RPC概述 微服务之间如何方便优雅的实…

计算机设计大赛 深度学习卷积神经网络垃圾分类系统 - 深度学习 神经网络 图像识别 垃圾分类 算法 小程序

文章目录 0 简介1 背景意义2 数据集3 数据探索4 数据增广(数据集补充)5 垃圾图像分类5.1 迁移学习5.1.1 什么是迁移学习&#xff1f;5.1.2 为什么要迁移学习&#xff1f; 5.2 模型选择5.3 训练环境5.3.1 硬件配置5.3.2 软件配置 5.4 训练过程5.5 模型分类效果(PC端) 6 构建垃圾…

数字电路 第一章—第二节(逻辑代数的基本概念、公式和定理)

一、基本逻辑关系举例 1、电路图 &#xff08;1&#xff09;与逻辑关系&#xff1a; &#xff08;2&#xff09;或逻辑关系&#xff1a; &#xff08;3&#xff09;非逻辑关系&#xff1a; 2、真值表 &#xff08;1&#xff09;在上述三种电路中&#xff0c;经过设定变量和状…

Nginx知识笔记

一、前言 首先&#xff0c;我们来看一张关于正向代理和反向代理的图片 简单理解正向代理和反向代理的概念&#xff1a; 正向代理&#xff1a;在客户端配置代理服务器(和跳板机功能类似&#xff0c;比如公司很多机器需要通过跳板机才允许登录&#xff0c;正向代理的典型用途是…

开源模型应用落地-工具使用篇-向量数据库进阶(四)

一、前言 通过学习"开源模型应用落地"系列文章&#xff0c;我们成功地建立了一个完整可实施的AI交付流程。现在&#xff0c;我们要引入向量数据库&#xff0c;作为我们AI服务的二级缓存。本文将继续基于上一篇“开源模型应用落地-工具使用篇-向量数据库&#xff08;三…

游泳耳机品牌排行榜前十名:十大爆款火热机型超高性价比

在当今这个科技日新月异的时代&#xff0c;游泳已经不再仅仅是一项简单的运动&#xff0c;而是一种生活方式的体现。随着人们对于健康生活的追求日益增强&#xff0c;游泳耳机也成为了许多游泳爱好者的必备装备之一。然而&#xff0c;市场上琳琅满目的游泳耳机品牌和型号让人眼…

网络安全8-11天笔记

内容安全&#xff1a; 攻击可能只是一个点&#xff0c;防御需要全方面进行。 IAE引擎&#xff1a; DFI和DPI技术&#xff1a;深度检测技术 DPI——深度包检测技术&#xff1a;主要针对完整的数据包&#xff08;数据包分片&#xff0c;分段需要重组&#xff09;&#xff0c;之…

2024年阿里云服务器优惠价格表和活动整理

2024阿里云服务器优惠活动政策整理&#xff0c;轻量2核2G3M服务器61元一年、2核4G4M带宽165元1年&#xff0c;云服务器4核16G10M带宽26元1个月、149元半年&#xff0c;阿里云ECS云服务器2核2G3M新老用户均可99元一年续费不涨价&#xff0c;企业用户2核4G5M带宽199元一年&#x…

Excel之index、MATCH面试题、VLOOKUP函数,

VLOOKUP() 在表格的首列查找指定的数值&#xff0c;并返回表格当前行中指定列处的数值。 结构&#xff1a;VLOOKUP(查找值,查找区域,列序数,匹配条件) 解释&#xff1a;VLOOKUP(找谁,在哪里找,第几列,0或1) 1.目的&#xff1a;根据【产品】查找【销量】 公式&#xff1a;V…

C++从入门到精通 第十二章(C++流)

写在前面&#xff1a; 本系列专栏主要介绍C的相关知识&#xff0c;思路以下面的参考链接教程为主&#xff0c;大部分笔记也出自该教程&#xff0c;笔者的原创部分主要在示例代码的注释部分。除了参考下面的链接教程以外&#xff0c;笔者还参考了其它的一些C教材&#xff08;比…

Vue 图片轮播第三方库 介绍

Vue图片轮播是一种在网页上以自动或手动方式展示图片的组件&#xff0c;常用于产品展示、网站banner等场景。有许多第三方库可以帮助Vue开发者轻松实现图片轮播功能。以下是一些流行的Vue图片轮播第三方库的介绍&#xff1a; 1. Vue-awesome-swiper - **简介**&#xff1a;V…

滚雪球学Java(70):深入理解Java中的PriorityQueue底层实现与源码分析

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

Linux之ACL权限管理

文章目录 1.ACL权限介绍二、操作步骤1. 添加测试目录、用户、组&#xff0c;并将用户添加到组2. 修改目录的所有者和所属组3. 设定权限4. 为临时用户分配权限5. 验证acl权限6. 控制组的acl权限 1.ACL权限介绍 每个项目成员有一个自己的项目目录&#xff0c;对自己的目录有完全…

【Django】Django自定义后台表单——对一个关联外键对象同时添加多个内容

以官方文档为例&#xff1a; 一个投票问题包含多个选项&#xff0c;基本的表单设计只能一个选项一个选项添加&#xff0c;效率较低&#xff0c;如何在表单设计中一次性添加多个关联选项&#xff1f; 示例代码&#xff1a; from django.contrib import adminfrom .models impo…

森林安全新保障:智能高压应急消防泵的应用

随着城市化进程的加快&#xff0c;森林资源的保护和利用日益受到重视。然而&#xff0c;森林火灾时有发生&#xff0c;给生态环境带来严重破坏。为了有效应对森林火灾&#xff0c;保障森林资源安全&#xff0c;智能高压森林应急消防泵应运而生&#xff0c;成为守护绿色生命的钢…

Python列表:灵活多变的数据结构

文章目录 一、列表1.创建列表2.访问列表元素3.修改列表元素4.添加元素5.删除元素 二、列表脚本操作符1.连接运算符 2.重复运算符 * 三、列表函数&方法1.函数1.1 len() 函数1.2 max() 函数1.3 min() 函数1.4 sum() 函数1.5 list() 函数 2.方法2.1 append() 方法2.2 extend()…

DSL Query基本语法

DSL Query基本语法 查询的基本语法如下&#xff1a; GET /indexName/_search {"query":{"查询类型":{"查询条件":"条件值"}} }查询所有 GET /indexName/_search {"query":{"match_all":{}} }match查询&#xf…