利用mybatis拦截器完成入库加密出库解密
在很多应用场景中,为了保证数据的安全性,我们需要对数据库中的敏感数据进行加密存储,在读取数据时再进行解密。MyBatis 是一个优秀的 Java 持久化框架,通过其拦截器机制,我们可以方便地实现入库加密和出库解密的功能。
一、MyBatis 拦截器简介
MyBatis 的拦截器机制允许我们在执行 SQL 语句的不同阶段进行干预。拦截器可以拦截的方法包括:
Executor
(执行器)的方法,如update
、query
等。StatementHandler
(语句处理器)的方法,如prepare
、parameterize
等。ResultSetHandler
(结果集处理器)的方法,如handleResultSets
、handleOutputParameters
等。
通过实现Interceptor
接口并配置拦截器,可以在不修改业务代码的情况下,对数据库操作进行增强。
二、实现入库加密出库解密的思路
- 对于入库操作,在
Executor
的update
方法拦截中,获取要插入或更新的数据,对敏感字段进行加密后再执行数据库操作。 - 对于出库操作,在
ResultSetHandler
的handleResultSets
方法拦截中,获取查询结果集,对敏感字段进行解密后再返回给业务层。
三、具体实现步骤
(一)定义加密和解密工具类
1. 此处省略:每个公司/企业等,使用的加密方式都有所不同,自行发挥.
(二)创建拦截器加密类
@Intercepts({
@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class)})
@Component
@Slf4j
public class SpecialInputParamInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("SpecialInputParamInterceptor拦截器执行了...");
log.info("SpecialInputParamInterceptor拦截器执行了...");
if (invocation.getTarget() instanceof ParameterHandler) {
ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
PreparedStatement ps = (PreparedStatement) invocation.getArgs()[0];
// 反射获取 参数对像
Field parameterField =
parameterHandler.getClass().getDeclaredField("parameterObject");
parameterField.setAccessible(true);
Object parameterObject = parameterField.get(parameterHandler);
if (Objects.nonNull(parameterObject)){
if (parameterObject instanceof Map) {
Map<?,?> paramMap = (Map<?,?>) parameterObject;
for (Object value : paramMap.values()) {
if (value instanceof Collection) {
Collection<?> collection = (Collection<?>) value;
for (Object item : collection) {
if (Objects.nonNull(item)){
Class<?> itemClass = item.getClass();
EncryptTransaction encryptDecryptClass = AnnotationUtils.findAnnotation(itemClass, EncryptTransaction.class);
if (Objects.nonNull(encryptDecryptClass)) {
//加密操作
}
}
}
}
break;
}
}else {
Class<?> parameterObjectClass = parameterObject.getClass();
EncryptTransaction encryptDecryptClass = AnnotationUtils.findAnnotation(parameterObjectClass, EncryptTransaction.class);
if (Objects.nonNull(encryptDecryptClass)){
Field[] declaredFields = parameterObjectClass.getDeclaredFields();
encryptFields(declaredFields, parameterObject,null);
}
}
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
private void encryptFields(Field[] declaredFields, Object parameter, SqlCommandType sqlCommandType) throws Exception {
for (Field field : declaredFields) {
Annotation annotation = field.getAnnotation(EncryptTransaction.class);
if (annotation != null) {
field.setAccessible(true);
Object originalValue = field.get(parameter);
if (originalValue!= null && originalValue instanceof String) {
String encryptedValue = encryptValue((String) originalValue);
field.set(parameter, encryptedValue);
}
}
}
}
private String encryptValue(String value) throws Exception {
return AESUtil.encrypt(value);
}
}
在这个拦截器中,当执行插入或更新操作时,对带有EncryptTransaction
注解的对象中的敏感字段进行加密。
(三)创建结果集处理进行解密拦截器类
@Intercepts({
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args={Statement.class})
})
@Component
@Slf4j
public class SpecialParamOutPutInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("SpecialParamOutPutInterceptor拦截器执行了...");
log.info("SpecialParamOutPutInterceptor拦截器执行了...");
Object result = invocation.proceed();
if (Objects.isNull(result)){
return null;
}
if (result instanceof ArrayList) {
ArrayList resultList = (ArrayList) result;
if (CollectionUtils.isNotEmpty(resultList) && needToDecrypt(resultList.get(0))){
for (Object item : resultList) {
if (item instanceof User || item instanceof UserVo) {
//解密操作
}
return result;
}
public boolean needToDecrypt(Object object){
Class<?> objectClass = object.getClass();
EncryptTransaction encryptDecryptClass = AnnotationUtils.findAnnotation(objectClass, EncryptTransaction.class);
if (Objects.nonNull(encryptDecryptClass)){
return true;
}
return false;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
private void decryptFields(Field[] declaredFields, Object parameter) throws Exception {
for (Field field : declaredFields) {
Annotation annotation = field.getAnnotation(EncryptTransaction.class);
if (annotation!= null) {
field.setAccessible(true);
Object originalValue = field.get(parameter);
if (originalValue!= null && originalValue instanceof String) {
String decryptedValue = decryptValue((String) originalValue);
field.set(parameter, decryptedValue);
}
}
}
}
private String decryptValue(String value) throws Exception {
return AESUtil.decrypt(value);
}
}
这个拦截器在查询结果集处理阶段,对带有EncryptTransaction
注解的对象中的敏感字段进行解密。
(四)配置拦截器
在 MyBatis 的配置文件中添加拦截器配置:
<configuration>
<plugins>
<plugin interceptor="com.example.EncryptionInterceptor">
<!-- 可以在这里配置拦截器的属性 -->
</plugin>
<plugin interceptor="com.example.DecryptionInterceptor">
<!-- 可以在这里配置拦截器的属性 -->
</plugin>
</plugins>
<!-- 其他配置 -->
</configuration>
!!!记得在实体类与字段上加自定义的注解
四、总结
通过 MyBatis 拦截器机制,我们可以轻松实现入库加密和出库解密的功能,增强了数据的安全性。在实际应用中,可以根据具体需求调整加密和解密算法,以及敏感字段的识别方式。同时,要注意拦截器的性能影响,避免过度复杂的处理逻辑导致性能下降。
以上就是利用 MyBatis 拦截器完成入库加密出库解密的一种实现方式
喜欢可以三连,有大哥可以赏赐,会欣喜不已,感谢!