mybatis拦截器ParameterHandler详解

目录

需求

实现

使用ParameterHandler拦截器

开启拦截器

spring方式

springboot方式

拦截器实现

方法解读

@Intercepts

@Signature

[public void setProperties(Properties properties)](#public void setProperties(Properties properties))


需求

客户要求入库的用户信息表中敏感信息,例如用户名和证件号等需要SM2国密加密后再入库。

实现

使用mybatis拦截器对入库的敏感信息进行拦截并加密,对查询的敏感信息进行解密。mybatis的拦截器执行的时间节点如下所示。

MyBatis执行流程:

配置文件加载 (Configuration)

创建 SqlSessionFactory

获取 SqlSession

获取 Mapper 代理对象

执行数据库操作

├── Executor 拦截器

├── ParameterHandler 拦截器

├── StatementHandler 拦截器

└── ResultSetHandler 拦截器

使用ParameterHandler拦截器

ParameterHandler是参数拦截器,主要用于拦截参数设置的过程,即拦截ParameterHandler.setParameters方法。这允许我们在执行SQL之前对参数进行修改或记录

开启拦截器

spring方式

XML 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 配置插件(拦截器) -->
    <plugins>
        <plugin interceptor="cfca.hke.privatization.dao.Intercepts.SubscriberTableParameterHandlerInterceptor"/>
    </plugins>
</configuration>

springboot方式

java 复制代码
@Configuration
public class MyBatisConfig {
    
    @Bean
    public DesensitizeInterceptor desensitizeInterceptor() {
        return new SubscriberTableParameterHandlerInterceptor();
    }
}

拦截器实现

代码如下

java 复制代码
@Component
@Intercepts({
        @Signature(
                type = ParameterHandler.class,
                method = "setParameters",
                args = {PreparedStatement.class}
        )
})
public class SubscriberTableParameterHandlerInterceptor implements Interceptor {


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //获取 parameterObject 对象
        ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
        Object parameterObject = parameterHandler.getParameterObject();

        // 执行拦截逻辑
        processDesensitize(parameterObject);
        return invocation.proceed();
    }

    /**
     * 加密处理
     */
    private void processDesensitize(Object parameter) throws Exception {
        if (parameter == null) return;

        // 处理不同类型的参数
        if (parameter instanceof Map) {
            // Map参数(如@Param注解)
            processMapParameter((Map<?, ?>) parameter);
        } else if (parameter instanceof Collection) {
            // 集合参数
            for (Object item : (Collection<?>) parameter) {
                processObject(item);
            }
        } else if (parameter.getClass().isArray()) {
            // 数组参数
            for (Object item : (Object[]) parameter) {
                processObject(item);
            }
        } else {
            // 单个对象参数
            processObject(parameter);
        }
    }

    /**
     * 处理Map参数
     */
    private void processMapParameter(Map<?, ?> parameterMap) throws Exception {
        for (Map.Entry<?, ?> entry : parameterMap.entrySet()) {
            Object value = entry.getValue();
            if (value != null) {
                processObject(value);
            }
        }
    }

    /**
     * 处理单个对象
     */
    private void processObject(Object obj) throws Exception {
        if (obj == null) return;
        Class<?> clazz = obj.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            if (field.getName().equals("subscriberName")) {
                field.setAccessible(true);
                Object fieldValue = field.get(obj);
                if (fieldValue instanceof String originalName) {
                    String EncName = getEncName(originalName);
                    // 设置新值
                    field.set(obj, EncName);
                }
            }
        }
    }


    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        // 从配置读取属性
    }

    /**
     * 获取加密机字符串
     *
     * @param name 用户名
     * @return String 加密后的值,带 Encrypted: 前缀
     */
    private String getEncName(String name) throws HKEException {
        String encName = HKECipher.encryptAndDecryptBySysKey(name);
        if (StringUtil.isEmpty(encName)) {
            throw new HKEException(ExceptionConstant.ERROR_CODE_862267);
        }
        String res = BaseConstant.ENCRYPTED_PREFIX + encName;
        if (res.length() > AdvancedValueConstant.DATABASE_LIMIT_SUBSCRIBER_NAME_NO) {
            throw new HKEException(ExceptionConstant.ERROR_CODE_862266);
        }
        return res;
    }
}

方法解读

@Intercepts

  • 标记类为 MyBatis 拦截器,服务启动的时候加载并生成拦截目标对应的代理类

  • 包含一个或多个 @Signature 注解

@Signature

java 复制代码
@Signature(
    type = ParameterHandler.class,              // 拦截的目标类
    method = "setParameters",                   // 拦截的方法名
    args = {PreparedStatement.class}            // 方法的参数类型
)

public void setProperties(Properties properties)

该方法可以从配置拦截器的地方读入指定的参数,以便更加灵活的控制拦截的逻辑,例如通过该方法读取配置中指定的表名,来实现不同的拦截器只对指定的表进行拦截操作等。

======================

喜欢求个关注,回归持续更新

======================

相关推荐
X-⃢_⃢-X3 分钟前
十、Redis之布隆过滤器
数据库·redis·缓存
Nturmoils5 分钟前
订单号查出了三笔,我以为是数据脏了,其实是自己写错了
数据库
ZKNOW甄知科技9 分钟前
燕千云深度集成飞书:以AI之力,开启无感IT运维体验
大数据·运维·网络·数据库·人工智能·低代码·集成学习
TDengine (老段)24 分钟前
TDengine 免费版说明
java·大数据·数据库·物联网·时序数据库·tdengine
汉知宝科技25 分钟前
历史案件数据迁移:知识产权管理系统落地的第一道门槛
大数据·数据库
码上有光28 分钟前
异常和智能指针
java·大数据·c++·servlet·异常·智能指针
这就是佬们吗35 分钟前
Python入门⑤-异常处理、文件操作与实战项目
开发语言·数据库·python·算法·pycharm
吴声子夜歌36 分钟前
MongoDB 4.x——SpringBoot框架整合
数据库·spring boot·mongodb
snow@li1 小时前
Spring Boot:项目服务器完整部署教程(零基础可直接实操)
服务器·spring boot·后端
Database_Cool_1 小时前
阿里云 Tair(企业级内存数据库)vs 腾讯云 Redis 云缓存深度对比:性能/数据结构/成本全维度 Benchmark
数据库·阿里云·缓存