目录
[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)
该方法可以从配置拦截器的地方读入指定的参数,以便更加灵活的控制拦截的逻辑,例如通过该方法读取配置中指定的表名,来实现不同的拦截器只对指定的表进行拦截操作等。
======================
喜欢求个关注,回归持续更新
======================