!479 update 优化 使用预扫描实体类提升代码性能
* 优化了数据库字段加解密的缓存机制.在自动配置类启动时,就把有加密注解的类进行缓存,以提高速度.
This commit is contained in:
parent
13e60a6048
commit
cad250f02a
@ -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>
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
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 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;
|
||||||
@ -8,7 +14,20 @@ 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.Resource;
|
||||||
|
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 +35,66 @@ 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
|
@Autowired
|
||||||
private EncryptorProperties properties;
|
private EncryptorProperties properties;
|
||||||
|
@Autowired
|
||||||
|
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) {
|
||||||
|
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
|
||||||
|
+ ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class");
|
||||||
|
for (Resource resource : resources) {
|
||||||
|
ClassMetadata classMetadata = new CachingMetadataReaderFactory().getMetadataReader(resource).getClassMetadata();
|
||||||
|
Class<?> clazz = Resources.classForName(classMetadata.getClassName());
|
||||||
|
Set<Field> encryptFieldSet = getEncryptFieldSetFromClazz(clazz);
|
||||||
|
if(CollectionUtil.isNotEmpty(encryptFieldSet)) {
|
||||||
|
fieldCache.put(clazz, encryptFieldSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch (Exception e) {
|
||||||
|
log.error("初始化数据安全缓存时出错:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
return fieldCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得一个类的加密字段集合
|
||||||
|
private Set<Field> getEncryptFieldSetFromClazz(Class<?> clazz) {
|
||||||
|
Set<Field> fieldSet = new HashSet<>();
|
||||||
|
// 判断clazz如果是接口,内部类,匿名类就直接返回
|
||||||
|
if (clazz.isInterface() || clazz.isMemberClass() || clazz.isAnonymousClass()) {
|
||||||
|
return fieldSet;
|
||||||
|
}
|
||||||
|
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 +106,8 @@ public class EncryptorAutoConfiguration {
|
|||||||
public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) {
|
public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) {
|
||||||
return new MybatisDecryptInterceptor(encryptorManager, properties);
|
return new MybatisDecryptInterceptor(encryptorManager, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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();
|
return null;
|
||||||
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;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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));
|
||||||
|
@ -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));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user