编写mybatis脱敏插件

简介: 编写mybatis脱敏插件

错误是不可避免的,但是不要重复错误——周恩来

首先贴成品链接:https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/275

使用方式:

在你的vo或者po/do上添加注解@Desensitization

可指定预设类型type为:cn.hutool.core.util.DesensitizedUtil.DesensitizedType

例如

@Desensitization(type = DesensitizedUtil.DesensitizedType.EMAIL)
private String email;

也可自定义正则表达式

@Desensitization(regex = "(?<=\\d{3})\\d(?=\\d{4})")
private String mobile;

还可以自定义处理器进行处理

@Desensitization(handler = MyDesensitizedHandler.class)
private String myField;

MyDesensitizedHandler实现cn.iocoder.yudao.framework.desensitization.core.handler.DesensitizationHandler即可

主要代码是这个拦截器:

package cn.iocoder.yudao.framework.desensitization.interceptor;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ReflectUtil;
import cn.iocoder.yudao.framework.desensitization.core.annotation.Desensitization;
import cn.iocoder.yudao.framework.desensitization.core.handler.DesensitizationHandler;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Collection;
import java.util.Objects;
import java.util.Properties;
/**
 * @author VampireAchao
 * @since 2022/10/6 11:24
 */
@Intercepts(@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}))
public class DesensitizationInterceptor extends JsqlParserSupport implements InnerInterceptor, Interceptor {
    /**
     * {@link StatementHandler#prepare(Connection, Integer)} 操作前置处理
     * <p>
     * 改改sql啥的
     *
     * @param sh                 StatementHandler(可能是代理对象)
     * @param connection         Connection
     * @param transactionTimeout transactionTimeout
     */
    @Override
    public 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 || sct == SqlCommandType.UPDATE) {
            PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
            mpBs.sql(parserMulti(mpBs.sql(), mpBs));
        }
    }
    @Override
    public void setProperties(Properties properties) {
        InnerInterceptor.super.setProperties(properties);
    }
    /**
     * 更新
     *
     * @param update
     * @param index
     * @param sql
     * @param obj
     */
    @Override
    protected void processUpdate(Update update, int index, String sql, Object obj) {
        if (!(obj instanceof PluginUtils.MPBoundSql)) {
            return;
        }
        PluginUtils.MPBoundSql boundSql = (PluginUtils.MPBoundSql) obj;
        Object parameterObject = boundSql.parameterObject();
        if (!(parameterObject instanceof MapperMethod.ParamMap<?>)) {
            return;
        }
        MapperMethod.ParamMap<?> paramMap = (MapperMethod.ParamMap<?>) parameterObject;
        Object entity = paramMap.get(Constants.ENTITY);
        if (!Objects.nonNull(entity)) {
            return;
        }
        processDesensitization(entity);
    }
    /**
     * 新增
     *
     * @param insert
     * @param index
     * @param sql
     * @param obj
     */
    @Override
    protected void processInsert(Insert insert, int index, String sql, Object obj) {
        if (!(obj instanceof PluginUtils.MPBoundSql)) {
            return;
        }
        PluginUtils.MPBoundSql boundSql = (PluginUtils.MPBoundSql) obj;
        Object entity = boundSql.parameterObject();
        processDesensitization(entity);
    }
    private void processDesensitization(Object entity) {
        for (Field field : ReflectUtil.getFields(entity.getClass())) {
            if (!field.isAnnotationPresent(Desensitization.class)) {
                continue;
            }
            Desensitization desensitization = field.getAnnotation(Desensitization.class);
            Object fieldValue = ReflectUtil.getFieldValue(entity, field);
            Opt.ofBlankAble(fieldValue).map(Object::toString).ifPresent(value -> {
                DesensitizationHandler desensitizationHandler = ReflectUtil.newInstance(desensitization.handler());
                String newValue = desensitizationHandler.apply(value, desensitization);
                ReflectUtil.setFieldValue(entity, field, newValue);
            });
        }
    }
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object proceed = invocation.proceed();
        if (Collection.class.isAssignableFrom(proceed.getClass())) {
            Collection<?> collection = (Collection<?>) proceed;
            collection.forEach(this::processDesensitization);
        }
        return proceed;
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }
}

po

package cn.iocoder.yudao.module.pojo.po;
import cn.hutool.core.util.DesensitizedUtil;
import cn.iocoder.yudao.framework.desensitization.core.annotation.Desensitization;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Tolerate;
/**
 * @author VampireAchao
 * @since 2022/10/6 15:19
 */
@Data
@Builder
public class UserInfo {
    @Tolerate
    public UserInfo() {
        // this is an accessible parameterless constructor.
    }
    private Long id;
    private String name;
    @Desensitization(type = DesensitizedUtil.DesensitizedType.EMAIL)
    private String email;
    @Desensitization(regex = "(?<=\\d{3})\\d(?=\\d{4})")
    private String mobile;
}

单元测试:

package cn.iocoder.yudao.module;
import cn.iocoder.yudao.framework.desensitization.config.YudaoDesensitizationAutoConfiguration;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.pojo.po.UserInfo;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.jdbc.Sql;
/**
 * @author VampireAchao
 * @since 2022/10/6 15:11
 */
@Import(YudaoDesensitizationAutoConfiguration.class)
@Sql(scripts = "/sql/insert_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) // 每个单元测试结束前,新增 DB
class DesensitizationTest extends BaseDbUnitTest {
    @Test
    void testQuery() {
        UserInfo userInfo = SqlHelper.execute(UserInfo.class, m -> m.selectById(1L));
        Assertions.assertEquals("123****8910", userInfo.getMobile());
        Assertions.assertEquals("a**************@gmail.com", userInfo.getEmail());
    }
    @Test
    void testUpdate() {
        UserInfo userInfo = UserInfo.builder().id(1L).mobile("12345678910").email("achao1441470436@gmail.com").build();
        SqlHelper.execute(UserInfo.class, m -> m.updateById(userInfo));
        Assertions.assertEquals("123****8910", userInfo.getMobile());
        Assertions.assertEquals("a**************@gmail.com", userInfo.getEmail());
    }
    @Test
    void testSave() {
        UserInfo userInfo = UserInfo.builder().name("张三").mobile("12345678910").email("achao1441470436@gmail.com").build();
        SqlHelper.execute(UserInfo.class, m -> m.insert(userInfo));
        Assertions.assertEquals("123****8910", userInfo.getMobile());
        Assertions.assertEquals("a**************@gmail.com", userInfo.getEmail());
    }
}
相关文章
|
4月前
|
SQL XML Java
8、Mybatis-Plus 分页插件、自定义分页
这篇文章介绍了Mybatis-Plus的分页功能,包括如何配置分页插件、使用Mybatis-Plus提供的Page对象进行分页查询,以及如何在XML中自定义分页SQL。文章通过具体的代码示例和测试结果,展示了分页插件的使用和自定义分页的方法。
8、Mybatis-Plus 分页插件、自定义分页
|
1月前
|
SQL Java 数据库连接
深入 MyBatis-Plus 插件:解锁高级数据库功能
Mybatis-Plus 提供了丰富的插件机制,这些插件可以帮助开发者更方便地扩展 Mybatis 的功能,提升开发效率、优化性能和实现一些常用的功能。
238 26
深入 MyBatis-Plus 插件:解锁高级数据库功能
|
1月前
|
SQL Java 数据库连接
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
MyBatis-Plus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。本文讲解了最新版MP的使用教程,包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段等核心功能。
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
|
1月前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
3月前
|
SQL Java 数据库连接
解决mybatis-plus 拦截器不生效--分页插件不生效
本文介绍了在使用 Mybatis-Plus 进行分页查询时遇到的问题及解决方法。依赖包包括 `mybatis-plus-boot-starter`、`mybatis-plus-extension` 等,并给出了正确的分页配置和代码示例。当分页功能失效时,需将 Mybatis-Plus 版本改为 3.5.5 并正确配置拦截器。
1083 6
解决mybatis-plus 拦截器不生效--分页插件不生效
|
3月前
|
SQL XML Java
springboot整合mybatis-plus及mybatis-plus分页插件的使用
这篇文章介绍了如何在Spring Boot项目中整合MyBatis-Plus及其分页插件,包括依赖引入、配置文件编写、SQL表创建、Mapper层、Service层、Controller层的创建,以及分页插件的使用和数据展示HTML页面的编写。
springboot整合mybatis-plus及mybatis-plus分页插件的使用
|
4月前
|
Java 数据库 Spring
MyBatisPlus分页插件在SpringBoot中的使用
这篇文章介绍了如何在Spring Boot项目中配置和使用MyBatis-Plus的分页插件,包括创建配置类以注册分页拦截器,编写测试类来演示如何进行分页查询,并展示了测试结果和数据库表结构。
MyBatisPlus分页插件在SpringBoot中的使用
|
5月前
|
SQL 监控 Java
IDEA插件-Mybatis Log Free日志替换
MyBatis Log Free 是一个免费的用于在 IntelliJ IDEA 中显示 MyBatis 日志的插件。它可以帮助您更方便地查看和分析 MyBatis 的 SQL 执行情况,以及定位潜在的性能问题,提高开发效率。
454 0
IDEA插件-Mybatis Log Free日志替换
|
6月前
MyBatis-Plus分页插件基于3.3.2
MyBatis-Plus分页插件基于3.3.2
103 0
MyBatis-Plus分页插件基于3.3.2
|
6月前
|
Java 数据库连接 数据库
mybatis自制插件+注解实现数据脱敏
mybatis自制插件+注解实现数据脱敏
165 1