背景
数据库字段内容需要加密,所以才需要给数据库内容加密,因为数据很机密,数据库也不能明文。
思考
保存和更新的时候,需要给对应的字段内容都给加密,但是这样代码里面的加密,地方需要维护很多代码,很不方便。
就想着写一个统一的方法,来做这件事情。
在Mybatis执行,insert或者update之前,给需要加密的字段加密。这里就想到了@Intercepts,拦截ParameterHandler.setParameters()方法,在设置参数之前,给对应的入参加密。
1.标识需要加密的字段
2.拦截器中,统一给字段数据加密
实现
拦截器代码如下:
import cn.bba.common.utils.SecurityUtil;
import cn.bba.foundation.annotation.ParameterEncrypt;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.MybatisDefaultParameterHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.SQLType;
import java.util.*;
@Slf4j
@Component
@Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),})
public class EncryptInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
if (invocation.getTarget() instanceof MybatisDefaultParameterHandler) {
MybatisDefaultParameterHandler parameterHandler = (MybatisDefaultParameterHandler) invocation.getTarget();
try {
parameterEncrypt(parameterHandler);
} catch (Exception e) {
log.error("sql parameter encrypt error!", e);
}
}
return invocation.proceed();
}
/**
* 给参数加密
*/
private static void parameterEncrypt(MybatisDefaultParameterHandler parameterHandler) throws Exception {
Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
parameterField.setAccessible(true);
// 获取入参
Object parameterObject = parameterField.get(parameterHandler);
if (parameterObject != null) {
Class<?> parameterClass = parameterObject.getClass();
// 获取方法上面的加密注解 (需要加密字段)
ParameterEncrypt parameterEncrypt = parameterClass.getAnnotation(ParameterEncrypt.class);
if (parameterEncrypt != null) {
for (String fieldStr : parameterEncrypt.fields()) {
Field field = parameterClass.getDeclaredField(fieldStr);
field.setAccessible(true);
Object possField = field.get(parameterObject);
if (possField != null) {
// List
if (possField instanceof List) {
List<String> resultList = (ArrayList) possField;
List<String> newList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(resultList)) {
for (String result : resultList) {
// 给入参通过AES加密,再放入
newList.add(SecurityUtil.encryptAes(result));
}
}
possField = newList;
} else if (possField instanceof Set) {
Set<String> resultList = (HashSet) possField;
Set<String> newSet = new HashSet<>();
if (CollectionUtils.isNotEmpty(resultList)) {
for (String result : resultList) {
// 给入参通过AES加密,再放入
newSet.add(SecurityUtil.encryptAes(result));
}
}
possField = newSet;
} else {
// 给入参通过AES加密,再放入
possField = SecurityUtil.encryptAes(possField.toString());
}
field.set(parameterObject, possField);
}
}
}
}
// 重新赋值引用
parameterField.set(parameterHandler, parameterObject);
}
/**
* 切记配置,否则当前拦截器不会加入拦截器链
*/
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
//自定义配置写入,没有自定义配置的可以直接置空此方法
@Override
public void setProperties(Properties properties) {
}
}
注解实现如下:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* 需要加密的类,添加此注解,fields,是需要加密的字段
*/
@Retention(value = RetentionPolicy.RUNTIME)
public @interface ParameterEncrypt {
// 只能添加,String List<String> Set<String> ,字段
String[] fields() default {};
}
只需要将@ParameterEncrypt,加在需要加密的类中,就可以实现加密