MyBatis-plus sql拦截器

news2025/1/6 18:22:45

因为业务需求,重新写了一套数据权限。项目中用的是mybtis-plus,正好MyBatis-Plus提供了插件数据权限插件 | MyBatis-Plus,那就根据文档来实现这个需求。

实现:

实现MultiDataPermissionHandler

首先创建MultiDataPermissionHandler的实现类,里面的内容自己做校验

@Slf4j
public class CustomDataPermissionHandler implements MultiDataPermissionHandler {

    // 岗位id
    private static final String CEO = "1";
    private static final String SE = "2";
    private static final String HR = "3";
    private static final String USER = "4";

    // 免校验表
    private static final String SYS = "sys";
    private static final Set<String> EXEMPT_TABLES = Set.of(
            "xxx不过滤的表",
            "xxx不过滤的表"
    );


    @Override
    public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {

        try {
            if (IgnoreWhereConditionAspect.shouldIgnoreWhereCondition()) {
                return null;
            }

            // 免校验表
            if (isExemptTable(table.getName())) {
                return null;
            }

            // 获取当前用户 未登录不做校验
            SysUser user = getCurrentUser();
            if (user == null || user.isAdmin()) {
                return null;
            }

            /* 获取岗位ids,总管可以看全部数据,主管可以看到自己小组的数据,其他只可以看到自己的数据 */
            List<String> postIds = getUserPostIds(user);
            if (postIds.contains(CEO)) {
                return null;
            }
            return postIds.contains(SE) ? parseGroupDataExpression(user.getGroupId(), getTableName(table)) : parseOwnDataExpression(getTableName(table));
        } catch (JSQLParserException e) {
            log.error("数据权限过滤失败," ,e);
            return null;
        }
    }

    /**
     * 获取表名
     * @param table 表
     * @return 表名
     */
    private String getTableName(Table table) {
        return String.valueOf(table.getAlias() == null ? table.getName() : table.getAlias().getName());
    }

    /**
     * 获取用户岗位ids
     * @param user 当前操作的用户
     * @return 岗位ids
     */
    private List<String> getUserPostIds(SysUser user) {
        if (user.getPostIds() == null) {
            throw new ServiceException(POST_MUST_NOT_NULL);
        }
        return Arrays.asList(user.getPostIds());
    }

    /**
     * 获取当前用户
     * @return 当前用户
     */
    private SysUser getCurrentUser() {
        try {
            return SecurityUtils.getUser();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 根据表名判断是否免校验
     * @param tableName 表名
     * @return 是否免校验
     */
    private boolean isExemptTable(String tableName) {
        return tableName.startsWith(SYS) || EXEMPT_TABLES.contains(tableName);
    }

    /**
     * 解析组数据表达式
     * @param groupId 组id
     * @return 组数据表达式
     */
    private Expression parseGroupDataExpression(String groupId, String tableName) throws JSQLParserException {
        return CCJSqlParserUtil.parseCondExpression(
                tableName + ".create_by in (select sug.user_id from sys_user su " +
                        "join sys_user_group sug on sug.user_id = su.id " +
                        "where sug.group_id = " + groupId + ")"
        );
    }

    /**
     * 解析自己的数据表达式
     * @return 自己的数据表达式
     */
    private Expression parseOwnDataExpression(String name) throws JSQLParserException {
        return CCJSqlParserUtil.parseCondExpression(name + ".create_by = " + SecurityUtils.getUserId());
    }

这里实现了MultiDataPermissionHandler接口的getSqlSegment方法,根据文档说明不需要拼接where条件的直接返回null就可以,需要拼接where添加的拼接相应的条件

注册数据权限拦截器

@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {

        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分页插件
        interceptor.addInnerInterceptor(paginationInnerInterceptor());
        // 乐观锁插件
        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
        // 阻断插件
        interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
        // 数据权限插件
        interceptor.addInnerInterceptor(new DataPermissionInterceptor(new CustomDataPermissionHandler()));

        return interceptor;
    }

    /**
     * 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html
     */
    public InnerInterceptor paginationInnerInterceptor() {
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
        // 设置数据库类型为mysql
        paginationInnerInterceptor.setDbType(DbType.MYSQL);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        paginationInnerInterceptor.setMaxLimit(-1L);
        return paginationInnerInterceptor;
    }

    /**
     * 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html
     */
    public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
        return new OptimisticLockerInnerInterceptor();
    }

    /**
     * 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html
     */
    public InnerInterceptor blockAttackInnerInterceptor() {
        return new BlockAttackInnerInterceptor();
    }
}

新建注解IgnoreWhereCondition

表示是否忽略sql拦截,默认为true,如果有些接口不需要鉴权就在controller上添加这个注解

@Target(ElementType.METHOD) // 该注解应用于方法
@Retention(RetentionPolicy.RUNTIME) // 运行时可用
public @interface IgnoreWhereCondition {
    boolean value() default true; // 默认值为 true,表示忽略
}

新建AOP切面 IgnoreWhereConditionAspect

/**
 * AOP切面,用于处理带有 @IgnoreWhereCondition 注解的方法。
 * 通过ThreadLocal存储当前方法是否需要忽略SQL拦截的状态。
 */
@Aspect
@Component
public class IgnoreWhereConditionAspect {

    private static final ThreadLocal<Boolean> ignoreWhereConditionHolder = ThreadLocal.withInitial(() -> false);

    /**
     * 在方法执行前检查是否有 @IgnoreWhereCondition 注解,并设置ThreadLocal中的状态。
     * @param joinPoint JoinPoint
     */
    @Before("@annotation(com.sh.framework.annotation.IgnoreWhereCondition)")
    public void beforeMethod(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        IgnoreWhereCondition annotation = method.getAnnotation(IgnoreWhereCondition.class);
        ignoreWhereConditionHolder.set(annotation.value());
    }

    /**
     * 获取当前线程中是否应该忽略SQL拦截。
     * 如果需要忽略,则清除ThreadLocal中的状态。
     * @return boolean
     */
    public static boolean shouldIgnoreWhereCondition() {
        Boolean b = ignoreWhereConditionHolder.get();
        if (b) clear();
        return b;
    }

    public static void clear() {
        ignoreWhereConditionHolder.remove();
    }
}

使用注解

@SelectRecordAnnotate(value = "分页查询付款申请")
@Operation(summary = "分页查询付款申请")
@GetMapping("selectPage")
@IgnoreWhereCondition
public AjaxResult selectPage(SiHePage page, CostPayReqVO costPayReqVO) {
    return success(costPayReqService.selectListPage(page, costPayReqVO));
}

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

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

相关文章

硬件-射频-PCB-常见天线分类-ESP32实例

文章目录 一&#xff1a;常见天线1.1 PCB天线①蓝牙模块的蛇形走线-天线②倒F天线-IFA&#xff1a;③蛇形倒F天线-MIFA④立体的倒F天线-PIFA 1.2 实例示意图1.21 对数周期天线(LPDA):1.22 2.4GHZ的八木天线&#xff1a;1.23 陶瓷天线&#xff1a;1.24 外接天线&#xff1a; 二&…

Gitlab-runner 修改默认的builds_dir并使用custom_build_dir配置

gitlab-runner 修改默认的builds_dir并使用custom_build_dir配置 1. 说明2. 实操&#xff08;以docker执行器为例&#xff09;2.1 修改默认的builds_dir2.1.1 调整gitlab-runner的配置文件2.1.2 CI文件 2.2 启用custom_build_dir2.2.1 调整gitlab-runner的配置文件2.2.2 CI文件…

WPF+Prism View与ViewModel绑定

1、开发环境&#xff0c;Win10VS2022.NET8Prism.DryIoc&#xff08;9.0.537&#xff09;或Prism.Unity。 2、通过NuGet安装Prism.DryIoc&#xff08;9.0.537&#xff09;或Prism.Unity。 2.1、创建ViewModels文件夹用于存放ViewModel文件、创建Views文件夹存放View文件。 将…

av1学习笔记(一):码流的整体框架

av1学习笔记&#xff08;一&#xff09;&#xff1a;码流的整体框架 目录 av1学习笔记&#xff08;一&#xff09;&#xff1a;码流的整体框架1. 码流结构分析&#xff1a;2. OBU信息分析2.1 obu_header2.2 obu_size2.3 drop_obu2.4 sequence_header_obu2.5 temporal_delimiter…

I2C(一):存储器模式:stm32作为主机对AT24C02写读数据

存储器模式&#xff1a;在HAL库中&#xff0c;I2C有专门对存储器外设设置的库函数 I2C&#xff08;一&#xff09;&#xff1a;存储器模式的使用 1、I2C轮询式写读AT24C02一页数据2、I2C轮询式写读AT24C02多页数据3、I2C中断式写读AT24C02一页数据4、I2C使用DMA式写读AT24C02一…

Ansys Discovery 中的网格划分方法:探索模式

本篇博客文章将介绍 Ansys Discovery 中可用于在探索模式下进行分析的网格划分方法。我们将在下一篇博客中介绍 Refine 模式下的网格划分技术。 了解 Discovery Explore 模式下的网格划分 网格划分是将几何模型划分为小单元以模拟系统在不同条件下的行为的过程。这是通过创建…

MySQL秘籍之索引与查询优化实战指南

MySQL秘籍之索引与查询优化实战指南 目录 MySQL秘籍之索引与查询优化实战指南相关阅读索引相关EXPLAIN 版本 1. 初级篇1.1 【练体术】基础1.1.1 库操作1.1.1 表操作创建一个表增加表字段 1.1.2 增删改插入一条数据删除一条数据更新一条数据库 1.1.3 查询查询所有数据条件查询&a…

MySQL8.0复制原理和部署配置步骤

1. mysql 主从复制原理 在从库上执行change master to&#xff1b;会将主库的信息保存到从库中的master.info文件中在从库执行start slave;开启io_thread, sql_thread线程;io_thread工作&#xff1b;io_thread通过master.info文件中主库的连接信息去连接主库&#xff1b;连接成…

智联视频超融合平台:电力行业的智能守护者

文章目录 一、远程实时监控与设备状态监测二、提高应急响应能力三、实现无人值守与减员增效四、保障电力设施安全与防范外部破坏五、提升电网运行管理效率与决策科学性六、助力电力企业数字化转型与智能化发展七、智联视频超融合平台 在当今数字化浪潮下&#xff0c;视频联网平…

上传本地项目或文件到SVN服务器(图片讲解,超简单)

上传本地项目或文件到SVN服务器&#xff08;图片讲解&#xff0c;超简单&#xff09; 1、使用TortoiseSVN2、输入SVN远程仓库地址3、添加文件或文件夹 需求&#xff1a;将本地的文件上传到SVN服务器上指定路径。前提&#xff1a;已经安装好TortoiseSVN 1、使用TortoiseSVN 右…

单周期CPU电路设计

1.实验目的 本实验旨在让学生通过设计一个简单的单周期 CPU 电路&#xff0c;深入理解 RISC-V 指令集的子集功能实现&#xff0c;掌握数字电路设计与实现的基本流程&#xff0c;包括指令解析、部件组合、电路设计以及功能仿真等环节&#xff0c;同时培养verilog HDL编程能力和…

ROS功能包开机自启动(2步解决)

为了实现小车在开机后能自动启动相关功能模块需要解决两个问题 1.准备启动脚本文件加载对应的rosnode和roslaunch&#xff0c;整合相关节点按需要顺序进行&#xff0c;防止报错 2.设置开启启动脚本相关内容 既然是自启动&#xff0c;不能避免USB数据传输的一些问题&#xff…

【ArcGISPro/GeoScenePro】解决常见的空间参考和投影问题

修复空间参考缺失的图像 数据 https://arcgis.com/sharing/rest/content/items/535efce0e3a04c8790ed7cc7ea96d02d/data 查看属性坐标 查看属性范围 范围值并不是零或接近于零。 这意味着栅格具有范围,因此其已正确进行

NLP 中文拼写检测纠正论文-08-Combining ResNet and Transformer

拼写纠正系列 NLP 中文拼写检测实现思路 NLP 中文拼写检测纠正算法整理 NLP 英文拼写算法&#xff0c;如果提升 100W 倍的性能&#xff1f; NLP 中文拼写检测纠正 Paper java 实现中英文拼写检查和错误纠正&#xff1f;可我只会写 CRUD 啊&#xff01; 一个提升英文单词拼…

【paddle】初次尝试

张量 张量是 paddlepaddle&#xff0c; torch&#xff0c; tensorflow 等 python 主流机器学习包中唯一通货变量&#xff0c;因此应当了解其基本的功能。 张量 paddle.Tensor 与 numpy.array 的转化 import paddle as paddle import matplotlib.pyplot as plt apaddle.to_t…

如何在谷歌浏览器中使用屏幕录制功能

在日常使用电脑的过程中&#xff0c;我们经常会遇到需要记录屏幕操作的情况。无论是制作教学视频、保存游戏过程还是记录会议内容&#xff0c;谷歌浏览器的屏幕录制功能都能帮助我们轻松实现这些需求。那么&#xff0c;如何在谷歌浏览器中启用并使用屏幕录制功能呢&#xff1f;…

万里数据库GreatSQL监控解析

GreatSQL是MySQL的一个分支&#xff0c;专注于提升MGR&#xff08;MySQL Group Replication&#xff09;的可靠性及性能。乐维监控平台可以有效地监控GreatSQL&#xff0c;帮助用户及时发现并解决潜在的性能问题。 通过在GreatSQL服务器上安装监控代理&#xff0c;收集数据库性…

APM 3.0.2 | 聚合B站、油管和MF的音乐播放器,支持歌词匹配

APM&#xff08;Azusa-Player-Mobile&#xff09;是一款基于B站的第三方音频播放器&#xff0c;现已扩展支持YouTube Music、YouTube、本地音乐、AList和MusicFree等平台。它不仅提供视频作为音频播放&#xff0c;还具备排行榜、分区动态等功能。用户可以通过添加Alist地址接入…

HTML——61. 单行文本框和密码输入框(主讲input元素的type属性)

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>单行文本框和密码输入框</title></head><body><!--input元素的type属性&#xff1a;(必须要有)--> <!--单行文本框:1.type"text"2.可…

在Typora中实现自动编号

文章目录 在Typora中实现自动编号1. 引言2. 准备工作3. 自动编号的实现3.1 文章大纲自动编号3.2 主题目录&#xff08;TOC&#xff09;自动编号3.3 文章内容自动编号3.4 完整代码 4. 应用自定义CSS5. 结论 在Typora中实现自动编号 1. 引言 Typora是一款非常流行的Markdown编辑…