mybatis拦截器实现自定义注解的敏感信息加密
2022-12-07 本文已影响0人
一二追
目录
1.开发环境与工具
2.自定义注解接口:
2.1敏感类的注解接口(SensitiveData)
2.2敏感字段的注解接口(SensitiveField)
2.3注解接口的用法
3.注解注入后需要写加解密的拦截器
3.1加密拦截器(EncryptInterceptor)
3.2解密拦截器(DecryptInterceptor)
4.configuration的jar包的拦截器的注册
1.开发环境与工具
java,idea,mybatis,spring boot
2.自定义注解接口: 2.1敏感类的注解接口(SensitiveData)
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import java.lang.annotation.*;
@documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface SensitiveData {
}
2.2敏感字段的注解接口(SensitiveField)
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import java.lang.annotation.*;
@documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface SensitiveField {
}
2.3注解接口的用法
举个例子,用户表(SysUser)中的手机(phone)字段需要进行加密处理,则需要在SysUser上加@SensitiveData进行注解,另外在phone字段上加@SensitiveField进行注解。
@SensitiveData
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("sys_user")
public class SysUser extends baseEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@Excel(name = "账号", width = 20)
private String account;
private String password;
@Excel(name = "姓名", width = 20)
private String name;
@SensitiveField
@Excel(name = "手机", width = 30)
private String phone;
private String salt;
}
3.注解注入后需要写加解密的拦截器
以上注解写完之后,我们需要写拦截器。让数据在后台存入数据库时进行拦截,拦截后将需要加密的字段在存入数据库之前使用SM4加密后再进行保存。同样的,在我们将数据从数据库中读出来时也需要进行拦截,拦截后使用SM4解密后拿到原值。
3.1加密拦截器(EncryptInterceptor)
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
@Component
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
@Signature(type=Executor.class,method="query",args={MappedStatement.class,Object.class, RowBounds.class, ResultHandler.class})
})
public class EncryptInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement statement = (MappedStatement) invocation.getArgs()[0];
// 获取该sql语句的类型,例如update,insert
String methodName = invocation.getMethod().getName();
// 获取该sql语句放入的参数
Object parameter = invocation.getArgs()[1];
if (StringUtils.equalsIgnoreCase("query", methodName)) {
Object result = invocation.proceed(); //执行请求方法,并将所得结果保存到result中
if (result instanceof ArrayList) {
ArrayList resultList = (ArrayList) result;
for (int i = 0; i < resultList.size(); i++) {
SensitiveData sensitiveData = AnnotationUtils.findAnnotation(resultList.get(i).getClass(), SensitiveData.class);
if (Objects.nonNull(sensitiveData)) {
Field[] declaredFields = resultList.get(i).getClass().getDeclaredFields();
}
}
}
return result;
}
// 拦截 Executor 的 update 方法 生成sql前将 tenantId 设置到实体中
if (StringUtils.equalsIgnoreCase("update", methodName) ||
StringUtils.equalsIgnoreCase("insert", methodName)) {
Class type = statement.getResultMaps().get(0).getClass();
if(parameter instanceof Map){
parameter = ((Map) parameter).get("param1");
}
SensitiveData sensitiveData = AnnotationUtils.findAnnotation(parameter.getClass(), SensitiveData.class);
if (Objects.nonNull(sensitiveData)) {
// 对参数内含注解的字段进行加密
Field[] declaredFields = parameter.getClass().getDeclaredFields();
Sm4crypto.encrypt(declaredFields, parameter);
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {
}
}
3.2解密拦截器(DecryptInterceptor)
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Properties;
@Component
@Intercepts({
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}),
})
public class DecryptInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//取出查询的结果
Object resultObject = invocation.proceed();
if (Objects.isNull(resultObject)) {
return null;
}
//基于selectList
if (resultObject instanceof ArrayList) {
ArrayList resultList = (ArrayList) resultObject;
if (!CollectionUtils.isEmpty(resultList) && needToDecrypt(resultList.get(0))) {
for (Object result : resultList) {
//逐一解密
Sm4crypto.decrypt(result);
}
}
//基于selectOne
} else {
if (needToDecrypt(resultObject)) {
Sm4crypto.decrypt(resultObject);
}
}
return resultObject;
}
private boolean needToDecrypt(Object object) {
Class objectClass = object.getClass();
SensitiveData sensitiveData = AnnotationUtils.findAnnotation(objectClass, SensitiveData.class);
return Objects.nonNull(sensitiveData);
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
4.拦截器用到的SM4相关类 4.1 SM4加解密组件(Sm4crypto)
/**
* 注册加密拦截器
*/
@Bean(value = "encryptInterceptor")
public EncryptInterceptor encryptInterceptor(@Qualifier("sensitiveManager") SensitiveManager sensitiveManager) {
EncryptInterceptor interceptor = new EncryptInterceptor(sensitiveManager);
Properties properties = new Properties();
// 可以调用properties.setProperty方法来给拦截器设置一些自定义参数
interceptor.setProperties(properties);
return interceptor;
}
/**
* 注册解密拦截器
*/
@Bean(value = "decryptInterceptor")
public DecryptInterceptor decryptInterceptor(@Qualifier("sensitiveManager") SensitiveManager sensitiveManager) {
DecryptInterceptor interceptor = new DecryptInterceptor(sensitiveManager);
Properties properties = new Properties();
// 可以调用properties.setProperty方法来给拦截器设置一些自定义参数
interceptor.setProperties(properties);
return interceptor;
}
@Bean(name = "filingSqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("filingDataSource") DataSource masterDataSource, @Qualifier("pageInterceptor") PageInterceptor pageInterceptor,@Qualifier("encryptInterceptor") EncryptInterceptor encryptInterceptor,@Qualifier("decryptInterceptor")DecryptInterceptor decryptInterceptor)
throws Exception {
final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(masterDataSource);
sessionFactory.setPlugins(pageInterceptor,encryptInterceptor,decryptInterceptor);
sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources(FilingAutoConfiguration.MAPPER_LOCATION));
return sessionFactory.getObject();
}