优化了数据库字段加解密的缓存机制.在自动配置类启动时,就把有加密注解的类进行缓存,以提高速度.

This commit is contained in:
_老马_ 2024-01-16 17:46:15 +08:00 committed by 老马
parent 13e60a6048
commit a98d0ba8eb
5 changed files with 107 additions and 23 deletions

View File

@ -42,6 +42,17 @@
<artifactId>spring-webmvc</artifactId> <artifactId>spring-webmvc</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,14 +1,32 @@
package org.dromara.common.encrypt.config; package org.dromara.common.encrypt.config;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.io.Resources;
import org.dromara.common.encrypt.annotation.EncryptField;
import org.dromara.common.encrypt.core.EncryptorManager; import org.dromara.common.encrypt.core.EncryptorManager;
import org.dromara.common.encrypt.interceptor.MybatisDecryptInterceptor; import org.dromara.common.encrypt.interceptor.MybatisDecryptInterceptor;
import org.dromara.common.encrypt.interceptor.MybatisEncryptInterceptor; import org.dromara.common.encrypt.interceptor.MybatisEncryptInterceptor;
import org.dromara.common.encrypt.properties.EncryptorProperties; import org.dromara.common.encrypt.properties.EncryptorProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.util.ClassUtils;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
import static org.springframework.util.StringUtils.tokenizeToStringArray;
/** /**
* 加解密配置 * 加解密配置
@ -16,17 +34,62 @@ import org.springframework.context.annotation.Bean;
* @author 老马 * @author 老马
* @version 4.6.0 * @version 4.6.0
*/ */
@AutoConfiguration @AutoConfiguration(after = MybatisPlusAutoConfiguration.class)
@EnableConfigurationProperties(EncryptorProperties.class) @EnableConfigurationProperties(EncryptorProperties.class)
@ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true") @ConditionalOnProperty(value = "mybatis-encryptor.enable", havingValue = "true")
@Slf4j
public class EncryptorAutoConfiguration { public class EncryptorAutoConfiguration {
@Autowired @Resource
private EncryptorProperties properties; private EncryptorProperties properties;
@Resource
private MybatisPlusProperties mybatisPlusProperties;
@Bean @Bean
public EncryptorManager encryptorManager() { public EncryptorManager encryptorManager() {
return new EncryptorManager(); Map<Class<?>, Set<Field>> fieldCache = scanEncryptClasses(mybatisPlusProperties.getTypeAliasesPackage());
return new EncryptorManager(fieldCache);
}
// 通过typeAliasesPackage设置的扫描包,来确定哪些实体类进行缓存
private Map<Class<?>, Set<Field>> scanEncryptClasses(String typeAliasesPackage) {
Map<Class<?>, Set<Field>> fieldCache = new HashMap<>();
try {
String[] packagePatternArray = tokenizeToStringArray(typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packagePattern : packagePatternArray) {
org.springframework.core.io.Resource[] resources = new PathMatchingResourcePatternResolver().getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class");
for (org.springframework.core.io.Resource resource : resources) {
ClassMetadata classMetadata = new CachingMetadataReaderFactory().getMetadataReader(resource).getClassMetadata();
Class<?> clazz = Resources.classForName(classMetadata.getClassName());
Set<Field> safeFieldSetFromClazz = getSafeFieldSetFromClazz(clazz);
if(CollectionUtil.isNotEmpty(safeFieldSetFromClazz)) {
fieldCache.put(clazz, safeFieldSetFromClazz);
}
}
}
}catch (Exception e) {
log.error("初始化数据安全缓存是出错:{}", e.getMessage());
}
return fieldCache;
}
// 获得一个类的加密字段集合
private Set<Field> getSafeFieldSetFromClazz(Class<?> clazz) {
Set<Field> fieldSet = new HashSet<>();
while (clazz != null) {
Field[] fields = clazz.getDeclaredFields();
fieldSet.addAll(Arrays.asList(fields));
clazz = clazz.getSuperclass();
}
fieldSet = fieldSet.stream().filter(field ->
field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
.collect(Collectors.toSet());
for (Field field : fieldSet) {
field.setAccessible(true);
}
return fieldSet;
} }
@Bean @Bean
@ -38,4 +101,8 @@ public class EncryptorAutoConfiguration {
public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) { public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) {
return new MybatisDecryptInterceptor(encryptorManager, properties); return new MybatisDecryptInterceptor(encryptorManager, properties);
} }
} }

View File

@ -1,16 +1,14 @@
package org.dromara.common.encrypt.core; package org.dromara.common.encrypt.core;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.encrypt.annotation.EncryptField;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/** /**
* 加密管理类 * 加密管理类
@ -19,6 +17,7 @@ import java.util.stream.Collectors;
* @version 4.6.0 * @version 4.6.0
*/ */
@Slf4j @Slf4j
@NoArgsConstructor
public class EncryptorManager { public class EncryptorManager {
/** /**
@ -31,25 +30,24 @@ public class EncryptorManager {
*/ */
Map<Class<?>, Set<Field>> fieldCache = new ConcurrentHashMap<>(); Map<Class<?>, Set<Field>> fieldCache = new ConcurrentHashMap<>();
/**
* 构造方法传入类加密字段缓存
*
* @param fieldCache 类加密字段缓存
*/
public EncryptorManager(Map<Class<?>, Set<Field>> fieldCache) {
this.fieldCache = fieldCache;
}
/** /**
* 获取类加密字段缓存 * 获取类加密字段缓存
*/ */
public Set<Field> getFieldCache(Class<?> sourceClazz) { public Set<Field> getFieldCache(Class<?> sourceClazz) {
return fieldCache.computeIfAbsent(sourceClazz, clazz -> { if(ObjectUtil.isNotNull(fieldCache)) {
Set<Field> fieldSet = new HashSet<>(); return fieldCache.get(sourceClazz);
while (clazz != null) {
Field[] fields = clazz.getDeclaredFields();
fieldSet.addAll(Arrays.asList(fields));
clazz = clazz.getSuperclass();
} }
fieldSet = fieldSet.stream().filter(field -> return null;
field.isAnnotationPresent(EncryptField.class) && field.getType() == String.class)
.collect(Collectors.toSet());
for (Field field : fieldSet) {
field.setAccessible(true);
}
return fieldSet;
});
} }
/** /**

View File

@ -73,7 +73,11 @@ public class MybatisDecryptInterceptor implements Interceptor {
list.forEach(this::decryptHandler); list.forEach(this::decryptHandler);
return; return;
} }
// 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错)
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass()); Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
if(ObjectUtil.isNull(fields)){
return;
}
try { try {
for (Field field : fields) { for (Field field : fields) {
field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field)); field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field));

View File

@ -82,7 +82,11 @@ public class MybatisEncryptInterceptor implements Interceptor {
list.forEach(this::encryptHandler); list.forEach(this::encryptHandler);
return; return;
} }
// 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错)
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass()); Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
if(ObjectUtil.isNull(fields)){
return;
}
try { try {
for (Field field : fields) { for (Field field : fields) {
field.set(sourceObject, this.encryptField(Convert.toStr(field.get(sourceObject)), field)); field.set(sourceObject, this.encryptField(Convert.toStr(field.get(sourceObject)), field));