mybatis、mybatis-plus插件开发,实现数据脱敏功能

首先说一下mybatis中四大组件的作用,下面开发的插件拦截器会使用

四大组件Executor、StatementHandler、ParameterHandler、ResultSetHandler

Executor:

Executor 是 MyBatis 中的执行器,负责 SQL 语句的执行工作。它通过调度 StatementHandler、ParameterHandler 和 ResultSetHandler 来完成 SQL 的增删改查操作。Executor 管理事务和缓存,可以是 SimpleExecutor、ReuseExecutor、BatchExecutor 或在开启二级缓存时的 CachingExecutor。

ParameterHandler:

ParameterHandler 用于处理 SQL 语句的参数设置。它将 Mapper 接口方法的参数封装,并负责将这些参数值传递给 PreparedStatement,以便进行预编译和执行。ParameterHandler 通过 TypeHandler 接口完成 Java 类型到 JDBC 类型的转换。

ResultSetHandler:

ResultSetHandler 负责处理 SQL 执行后的返回结果集(ResultSet)。它将结果集转换并映射到 Java 对象,完成从数据库字段到 Java 实体属性的映射工作。ResultSetHandler 能够处理多种结果集的复杂映射关系。

StatementHandler:

StatementHandler 是 MyBatis 中的语句处理器,它是四大对象的核心,起到承上启下的作用。StatementHandler 负责使用 JDBC 的 Statement(包括 PreparedStatement 和 CallableStatement)执行实际的数据库操作。它通过调用 ParameterHandler 来设置 SQL 参数,并通过调用 ResultSetHandler 来处理查询结果。

实现思路:

1 首先我们需要对数据的结果集进行拦截,也就是说需要拦截这个接口的方法

2 得到数据返回的结果集后,对结果集转换成 List 进行遍历脱敏

3 脱敏的时候我们还需要判断一下哪些字段需要进行脱敏,这里我们定义注解来标识需要脱敏的字段(在这个注解里面可以定义一些脱敏策略,比如对手机号的脱敏规则、身份证的脱敏规则等等)

4 遍历的时候通过反射,获取所有属性

5 我这里的脱敏有三个条件

a、必须带有 脱敏标识注解

b、必须是 String 类型
c、不得为空,这里直接使用工具类来判断 StringUtils.isEmpty()

6 脱敏条件达成后,就获取该注解上的 脱敏策略,根据脱敏策略 对属性进行脱敏

7 将脱敏后的结果重新设置到属性上


具体实现步骤

1 定义注解和不同的脱敏策略

import java.util.function.Function;

//这里的Function是jdk8新特性,自己了解一哈子
//提示:传入一个函数执行
public interface Desensitizer extends Function<String,String> {

}

//这里定义脱敏策略
public enum TuoMinStrategy {
    
    /*手机号*/
    PHONE(s->s.replaceAll("^(\\\\d{3})\\\\d{4}(\\\\d{4})$","$1****$2")),
    /*用户名 匹配中文全部替换*/
    USERNAME(s->s.replaceAll("[\\u4e00-\\u9fa5]","*"));

    private final Desensitizer desensitizer;

    TuoMinStrategy(Desensitizer d) {
        desensitizer = d;
    }

    public Desensitizer getDesensitizer() {
        return desensitizer;
    }
}

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//作用于属性上
@Target(ElementType.FIELD)
//运行时生效
@Retention(RetentionPolicy.RUNTIME)
public @interface TuoMin {

    /**
     * 脱敏策略
     * @return
     */
    TuoMinStrategy strategy();

}

2 脱敏插件核心类

注意:注解中的属性,method和args不是硬背的,有技巧,如这个,
ResultSetHandler类,点进去,要拦截哪个方法,方法名直接复制,方法里的参数,直接copy 引用

import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.List;
import java.util.stream.Stream;

@Component //需要放入到 ioc 容器中拦截器才会生效哦
//指定要拦截的目标方法的注解
//<h1>1、这个就是指定需要拦截的方法,这里我们指定拦截返回结果集的方法</h1>
@Intercepts(@Signature(type = ResultSetHandler.class,method = "handleResultSets",args= Statement.class))
public class TongMinPlugin implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
       	//<h1>2、得到数据返回的结果集,对结果集转换成 List<Object> 进行遍历脱敏</h1>
        List<Object> records = (List<Object>) invocation.proceed();
        System.out.println("records = " + records);
        // 遍历数据 调用 tuoMin 这个方法
        records.forEach(this::tuoMin);
        return records;
    }

    private void tuoMin(Object source) {
        Class<?> sourceClass = source.getClass();
        System.out.println("sourceClass = " + sourceClass);
        MetaObject metaObject = SystemMetaObject.forObject(source);//这个东东是 mybatis 提供的,通过这个玩意我们可以得到属性的值
        System.out.println("metaObject = " + metaObject);
        // 使用 stream 流
        /**
         * 1、得到所有属性
         * 2、筛选出带有 TuoMin.class 注解的属性
         * 3、调用doTuoMin方法将这些属性脱敏
         */
        Stream.of(sourceClass.getDeclaredFields())
                .filter(field -> field.isAnnotationPresent(TuoMin.class))
                .forEach(field->doTuoMin(metaObject,field));
    }

    private void doTuoMin(MetaObject metaObject, Field field) {
        System.out.println("metaObject = " + metaObject);
        System.out.println("field = " + field);

        String name = field.getName();
        System.out.println("name = " + name);
        Object value = metaObject.getValue(name);//根据属性名得到属性值
        System.out.println("value = " + value);
        // 脱敏条件:必须为String类型,值不等于空
        if (String.class == metaObject.getGetterType(name) && StringUtils.hasText(value.toString())) {
            //获取脱敏策略
            TuoMin tuoMin = field.getAnnotation(TuoMin.class);
            System.out.println("tuoMin = " + tuoMin);
            TuoMinStrategy type = tuoMin.strategy();
            System.out.println("type = " + type);
            // 调用策略正则表达式脱敏,得到结果
            Object apply = type.getDesensitizer().apply((String) value);
            System.out.println("apply = " + apply);
            // 将脱敏后的结果重新设置到属性上
            metaObject.setValue(name,apply);
        }
    }

}

3 在需要脱敏的pojo的字段上加上注解,如

@TableField(value = "title")
@TuoMin(strategy = TuoMinStrategy.USERNAME) //加上注解并指定脱敏策略
private String title;

4 测试,举例

脱敏前:
张三达美

脱敏后:
张 * * 美

部分文字和代码引用博文
【MyBatis】脱敏插件_前端脱敏插件是什么-CSDN博客

相关推荐
hanbarger4 小时前
mybatis框架——缓存,分页
java·spring·mybatis
乘风御浪云帆之上10 小时前
数据库操作【JDBC & HIbernate & Mybatis】
数据库·mybatis·jdbc·hibernate
向阳12181 天前
mybatis 动态 SQL
数据库·sql·mybatis
新手小袁_J1 天前
JDK11下载安装和配置超详细过程
java·spring cloud·jdk·maven·mybatis·jdk11
xlsw_2 天前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
cmdch20172 天前
Mybatis加密解密查询操作(sql前),where要传入加密后的字段时遇到的问题
数据库·sql·mybatis
秋恬意2 天前
什么是MyBatis
mybatis
CodeChampion2 天前
60.基于SSM的个人网站的设计与实现(项目 + 论文)
java·vue.js·mysql·spring·elementui·node.js·mybatis
ZWZhangYu3 天前
【MyBatis源码分析】使用 Java 动态代理,实现一个简单的插件机制
java·python·mybatis
程序员大金3 天前
基于SSM+Vue的个性化旅游推荐系统
前端·vue.js·mysql·java-ee·tomcat·mybatis·旅游