refactor(common-encrypt): 优化加解密处理流程
- 抽取 EncryptContextFactory 与 EncryptedFieldProcessor,统一加密上下文创建和字段处理逻辑 - 修复 MyBatis 入参加密后未恢复原对象的问题,避免调用方对象被密文化 - 移除结果解密拦截器对 MyBatis 内部 parameterHandler 的反射依赖 - 优化 EncryptorManager 缓存 key 与懒加载字段扫描逻辑 - CryptoFilter 改为构造注入 Spring MVC 依赖,避免请求期间动态获取 Bean - 修复请求/响应包装器字符集、流状态和响应头处理 - 响应加密不再 reset 原始响应,避免丢失状态码和已有响应头 - 为加密配置补充默认值和启动校验
This commit is contained in:
+6
-2
@@ -9,6 +9,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||||||
import org.springframework.boot.web.servlet.FilterRegistration;
|
import org.springframework.boot.web.servlet.FilterRegistration;
|
||||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* api 解密自动配置
|
* api 解密自动配置
|
||||||
@@ -27,8 +29,10 @@ public class ApiDecryptAutoConfiguration {
|
|||||||
order = FilterRegistrationBean.HIGHEST_PRECEDENCE,
|
order = FilterRegistrationBean.HIGHEST_PRECEDENCE,
|
||||||
dispatcherTypes = DispatcherType.REQUEST
|
dispatcherTypes = DispatcherType.REQUEST
|
||||||
)
|
)
|
||||||
public CryptoFilter cryptoFilter(ApiDecryptProperties properties) {
|
public CryptoFilter cryptoFilter(ApiDecryptProperties properties,
|
||||||
return new CryptoFilter(properties);
|
RequestMappingHandlerMapping requestMappingHandlerMapping,
|
||||||
|
HandlerExceptionResolver handlerExceptionResolver) {
|
||||||
|
return new CryptoFilter(properties, requestMappingHandlerMapping, handlerExceptionResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-6
@@ -3,7 +3,11 @@ package org.dromara.common.encrypt.config;
|
|||||||
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
|
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
|
||||||
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
|
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import org.dromara.common.encrypt.core.EncryptContextFactory;
|
||||||
|
import org.dromara.common.encrypt.core.EncryptedFieldProcessor;
|
||||||
import org.dromara.common.encrypt.core.EncryptorManager;
|
import org.dromara.common.encrypt.core.EncryptorManager;
|
||||||
|
import org.dromara.common.encrypt.enums.AlgorithmType;
|
||||||
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;
|
||||||
@@ -16,8 +20,7 @@ import org.springframework.context.annotation.Bean;
|
|||||||
/**
|
/**
|
||||||
* 加解密配置
|
* 加解密配置
|
||||||
*
|
*
|
||||||
* @author 老马
|
* @author Lion Li
|
||||||
* @version 4.6.0
|
|
||||||
*/
|
*/
|
||||||
@AutoConfiguration(after = MybatisPlusAutoConfiguration.class)
|
@AutoConfiguration(after = MybatisPlusAutoConfiguration.class)
|
||||||
@EnableConfigurationProperties(EncryptorProperties.class)
|
@EnableConfigurationProperties(EncryptorProperties.class)
|
||||||
@@ -30,17 +33,42 @@ public class EncryptorAutoConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public EncryptorManager encryptorManager(MybatisPlusProperties mybatisPlusProperties) {
|
public EncryptorManager encryptorManager(MybatisPlusProperties mybatisPlusProperties) {
|
||||||
|
validateEncryptorProperties(properties);
|
||||||
return new EncryptorManager(mybatisPlusProperties.getTypeAliasesPackage());
|
return new EncryptorManager(mybatisPlusProperties.getTypeAliasesPackage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorManager encryptorManager) {
|
public EncryptContextFactory encryptContextFactory() {
|
||||||
return new MybatisEncryptInterceptor(encryptorManager, properties);
|
return new EncryptContextFactory(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) {
|
public EncryptedFieldProcessor encryptedFieldProcessor(EncryptorManager encryptorManager, EncryptContextFactory encryptContextFactory) {
|
||||||
return new MybatisDecryptInterceptor(encryptorManager, properties);
|
return new EncryptedFieldProcessor(encryptorManager, encryptContextFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptedFieldProcessor encryptedFieldProcessor) {
|
||||||
|
return new MybatisEncryptInterceptor(encryptedFieldProcessor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptedFieldProcessor encryptedFieldProcessor) {
|
||||||
|
return new MybatisDecryptInterceptor(encryptedFieldProcessor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateEncryptorProperties(EncryptorProperties properties) {
|
||||||
|
AlgorithmType algorithm = properties.getAlgorithm();
|
||||||
|
if (algorithm == AlgorithmType.AES || algorithm == AlgorithmType.SM4) {
|
||||||
|
if (StringUtils.isBlank(properties.getPassword())) {
|
||||||
|
throw new IllegalArgumentException("mybatis-encryptor.password 不能为空");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (algorithm == AlgorithmType.RSA || algorithm == AlgorithmType.SM2) {
|
||||||
|
if (StringUtils.isAnyBlank(properties.getPublicKey(), properties.getPrivateKey())) {
|
||||||
|
throw new IllegalArgumentException("mybatis-encryptor.publicKey 与 privateKey 不能为空");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
package org.dromara.common.encrypt.core;
|
||||||
|
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import org.dromara.common.encrypt.annotation.EncryptField;
|
||||||
|
import org.dromara.common.encrypt.enums.AlgorithmType;
|
||||||
|
import org.dromara.common.encrypt.enums.EncodeType;
|
||||||
|
import org.dromara.common.encrypt.properties.EncryptorProperties;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密上下文工厂。
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
public class EncryptContextFactory {
|
||||||
|
|
||||||
|
private final EncryptorProperties defaultProperties;
|
||||||
|
|
||||||
|
public EncryptContextFactory(EncryptorProperties defaultProperties) {
|
||||||
|
this.defaultProperties = defaultProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据字段注解和默认配置创建加密上下文。
|
||||||
|
*
|
||||||
|
* @param field 加密字段
|
||||||
|
* @return 加密上下文
|
||||||
|
*/
|
||||||
|
public EncryptContext create(Field field) {
|
||||||
|
EncryptField encryptField = field.getAnnotation(EncryptField.class);
|
||||||
|
EncryptContext encryptContext = new EncryptContext();
|
||||||
|
encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
|
||||||
|
encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());
|
||||||
|
encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());
|
||||||
|
encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());
|
||||||
|
encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());
|
||||||
|
return encryptContext;
|
||||||
|
}
|
||||||
|
}
|
||||||
+109
@@ -0,0 +1,109 @@
|
|||||||
|
package org.dromara.common.encrypt.core;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密字段处理器。
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class EncryptedFieldProcessor {
|
||||||
|
|
||||||
|
private final EncryptorManager encryptorManager;
|
||||||
|
private final EncryptContextFactory contextFactory;
|
||||||
|
|
||||||
|
public EncryptedFieldProcessor(EncryptorManager encryptorManager, EncryptContextFactory contextFactory) {
|
||||||
|
this.encryptorManager = encryptorManager;
|
||||||
|
this.contextFactory = contextFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密对象字段,并返回原始字段快照。
|
||||||
|
*
|
||||||
|
* @param sourceObject 待加密对象
|
||||||
|
* @return 原始字段快照
|
||||||
|
*/
|
||||||
|
public List<FieldSnapshot> encrypt(Object sourceObject) {
|
||||||
|
List<FieldSnapshot> snapshots = new ArrayList<>();
|
||||||
|
handle(sourceObject, Collections.newSetFromMap(new IdentityHashMap<>()), (target, field, value) -> {
|
||||||
|
String encrypt = encryptorManager.encrypt(value, contextFactory.create(field));
|
||||||
|
if (!Objects.equals(value, encrypt)) {
|
||||||
|
snapshots.add(new FieldSnapshot(target, field, value));
|
||||||
|
field.set(target, encrypt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return snapshots;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解密对象字段。
|
||||||
|
*
|
||||||
|
* @param sourceObject 待解密对象
|
||||||
|
*/
|
||||||
|
public void decrypt(Object sourceObject) {
|
||||||
|
handle(sourceObject, Collections.newSetFromMap(new IdentityHashMap<>()), (target, field, value) ->
|
||||||
|
field.set(target, encryptorManager.decrypt(value, contextFactory.create(field))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handle(Object sourceObject, Set<Object> visited, FieldHandler fieldHandler) {
|
||||||
|
if (ObjectUtil.isNull(sourceObject) || sourceObject instanceof String || visited.contains(sourceObject)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
visited.add(sourceObject);
|
||||||
|
if (sourceObject instanceof Map<?, ?> map) {
|
||||||
|
new HashSet<>(map.values()).forEach(value -> handle(value, visited, fieldHandler));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (sourceObject instanceof Collection<?> collection) {
|
||||||
|
if (CollUtil.isEmpty(collection)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
collection.forEach(item -> handle(item, visited, fieldHandler));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
|
||||||
|
if (CollUtil.isEmpty(fields)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
for (Field field : fields) {
|
||||||
|
String value = Convert.toStr(field.get(sourceObject));
|
||||||
|
if (ObjectUtil.isNotNull(value)) {
|
||||||
|
fieldHandler.handle(sourceObject, field, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理加密字段时出错", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface FieldHandler {
|
||||||
|
|
||||||
|
void handle(Object target, Field field, String value) throws IllegalAccessException;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段原始值快照。
|
||||||
|
*/
|
||||||
|
public record FieldSnapshot(Object target, Field field, Object value) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 恢复原始字段值。
|
||||||
|
*/
|
||||||
|
public void restore() {
|
||||||
|
try {
|
||||||
|
field.set(target, value);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
log.error("恢复加密字段时出错", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
-16
@@ -2,13 +2,13 @@ package org.dromara.common.encrypt.core;
|
|||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
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.apache.ibatis.io.Resources;
|
import org.apache.ibatis.io.Resources;
|
||||||
import org.dromara.common.core.constant.Constants;
|
import org.dromara.common.core.constant.Constants;
|
||||||
import org.dromara.common.core.utils.ObjectUtils;
|
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
import org.dromara.common.encrypt.annotation.EncryptField;
|
import org.dromara.common.encrypt.annotation.EncryptField;
|
||||||
|
import org.dromara.common.encrypt.enums.AlgorithmType;
|
||||||
|
import org.dromara.common.encrypt.enums.EncodeType;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||||
@@ -28,17 +28,15 @@ import java.util.stream.Collectors;
|
|||||||
/**
|
/**
|
||||||
* 加密管理类
|
* 加密管理类
|
||||||
*
|
*
|
||||||
* @author 老马
|
* @author Lion Li
|
||||||
* @version 4.6.0
|
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@NoArgsConstructor
|
|
||||||
public class EncryptorManager {
|
public class EncryptorManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缓存加密器
|
* 缓存加密器
|
||||||
*/
|
*/
|
||||||
Map<Integer, IEncryptor> encryptorMap = new ConcurrentHashMap<>();
|
Map<EncryptorCacheKey, IEncryptor> encryptorMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 类加密字段缓存
|
* 类加密字段缓存
|
||||||
@@ -59,7 +57,13 @@ public class EncryptorManager {
|
|||||||
* 获取类加密字段缓存
|
* 获取类加密字段缓存
|
||||||
*/
|
*/
|
||||||
public Set<Field> getFieldCache(Class<?> sourceClazz) {
|
public Set<Field> getFieldCache(Class<?> sourceClazz) {
|
||||||
return ObjectUtils.notNullGetter(fieldCache, f -> f.get(sourceClazz));
|
if (sourceClazz == null || sourceClazz.isPrimitive() || sourceClazz.isArray()
|
||||||
|
|| sourceClazz.isEnum() || sourceClazz.isAnnotation()
|
||||||
|
|| ClassUtils.isPrimitiveOrWrapper(sourceClazz)
|
||||||
|
|| sourceClazz.getName().startsWith("java.")) {
|
||||||
|
return Set.of();
|
||||||
|
}
|
||||||
|
return fieldCache.computeIfAbsent(sourceClazz, this::getEncryptFieldSetFromClazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,13 +72,9 @@ public class EncryptorManager {
|
|||||||
* @param encryptContext 加密执行者需要的相关配置参数
|
* @param encryptContext 加密执行者需要的相关配置参数
|
||||||
*/
|
*/
|
||||||
public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) {
|
public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) {
|
||||||
int key = encryptContext.hashCode();
|
EncryptorCacheKey key = EncryptorCacheKey.of(encryptContext);
|
||||||
if (encryptorMap.containsKey(key)) {
|
return encryptorMap.computeIfAbsent(key, cacheKey ->
|
||||||
return encryptorMap.get(key);
|
ReflectUtil.newInstance(cacheKey.algorithm().getClazz(), encryptContext));
|
||||||
}
|
|
||||||
IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext);
|
|
||||||
encryptorMap.put(key, encryptor);
|
|
||||||
return encryptor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,7 +83,7 @@ public class EncryptorManager {
|
|||||||
* @param encryptContext 加密执行者需要的相关配置参数
|
* @param encryptContext 加密执行者需要的相关配置参数
|
||||||
*/
|
*/
|
||||||
public void removeEncryptor(EncryptContext encryptContext) {
|
public void removeEncryptor(EncryptContext encryptContext) {
|
||||||
this.encryptorMap.remove(encryptContext.hashCode());
|
this.encryptorMap.remove(EncryptorCacheKey.of(encryptContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,6 +120,9 @@ public class EncryptorManager {
|
|||||||
* 通过 typeAliasesPackage 设置的扫描包 扫描缓存实体
|
* 通过 typeAliasesPackage 设置的扫描包 扫描缓存实体
|
||||||
*/
|
*/
|
||||||
private void scanEncryptClasses(String typeAliasesPackage) {
|
private void scanEncryptClasses(String typeAliasesPackage) {
|
||||||
|
if (StringUtils.isBlank(typeAliasesPackage)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||||
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
|
CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
|
||||||
String[] packagePatternArray = StringUtils.splitPreserveAllTokens(typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
String[] packagePatternArray = StringUtils.splitPreserveAllTokens(typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
||||||
@@ -149,7 +152,7 @@ public class EncryptorManager {
|
|||||||
Set<Field> fieldSet = new HashSet<>();
|
Set<Field> fieldSet = new HashSet<>();
|
||||||
// 判断clazz如果是接口,内部类,匿名类就直接返回
|
// 判断clazz如果是接口,内部类,匿名类就直接返回
|
||||||
if (clazz.isInterface() || clazz.isMemberClass() || clazz.isAnonymousClass()) {
|
if (clazz.isInterface() || clazz.isMemberClass() || clazz.isAnonymousClass()) {
|
||||||
return fieldSet;
|
return Set.of();
|
||||||
}
|
}
|
||||||
while (clazz != null) {
|
while (clazz != null) {
|
||||||
Field[] fields = clazz.getDeclaredFields();
|
Field[] fields = clazz.getDeclaredFields();
|
||||||
@@ -165,4 +168,22 @@ public class EncryptorManager {
|
|||||||
return fieldSet;
|
return fieldSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record EncryptorCacheKey(
|
||||||
|
AlgorithmType algorithm,
|
||||||
|
EncodeType encode,
|
||||||
|
String password,
|
||||||
|
String publicKey,
|
||||||
|
String privateKey
|
||||||
|
) {
|
||||||
|
|
||||||
|
private static EncryptorCacheKey of(EncryptContext encryptContext) {
|
||||||
|
return new EncryptorCacheKey(
|
||||||
|
encryptContext.getAlgorithm(),
|
||||||
|
encryptContext.getEncode(),
|
||||||
|
encryptContext.getPassword(),
|
||||||
|
encryptContext.getPublicKey(),
|
||||||
|
encryptContext.getPrivateKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-7
@@ -6,7 +6,6 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.dromara.common.core.constant.HttpStatus;
|
import org.dromara.common.core.constant.HttpStatus;
|
||||||
import org.dromara.common.core.exception.ServiceException;
|
import org.dromara.common.core.exception.ServiceException;
|
||||||
import org.dromara.common.core.utils.SpringUtils;
|
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
import org.dromara.common.encrypt.annotation.ApiEncrypt;
|
import org.dromara.common.encrypt.annotation.ApiEncrypt;
|
||||||
import org.dromara.common.encrypt.properties.ApiDecryptProperties;
|
import org.dromara.common.encrypt.properties.ApiDecryptProperties;
|
||||||
@@ -27,9 +26,15 @@ import java.io.IOException;
|
|||||||
*/
|
*/
|
||||||
public class CryptoFilter implements Filter {
|
public class CryptoFilter implements Filter {
|
||||||
private final ApiDecryptProperties properties;
|
private final ApiDecryptProperties properties;
|
||||||
|
private final RequestMappingHandlerMapping requestMappingHandlerMapping;
|
||||||
|
private final HandlerExceptionResolver handlerExceptionResolver;
|
||||||
|
|
||||||
public CryptoFilter(ApiDecryptProperties properties) {
|
public CryptoFilter(ApiDecryptProperties properties,
|
||||||
|
RequestMappingHandlerMapping requestMappingHandlerMapping,
|
||||||
|
HandlerExceptionResolver handlerExceptionResolver) {
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
|
this.requestMappingHandlerMapping = requestMappingHandlerMapping;
|
||||||
|
this.handlerExceptionResolver = handlerExceptionResolver;
|
||||||
EncryptUtils.validateRsaPublicKey(properties.getPublicKey());
|
EncryptUtils.validateRsaPublicKey(properties.getPublicKey());
|
||||||
EncryptUtils.validateRsaPrivateKey(properties.getPrivateKey());
|
EncryptUtils.validateRsaPrivateKey(properties.getPrivateKey());
|
||||||
}
|
}
|
||||||
@@ -55,8 +60,7 @@ public class CryptoFilter implements Filter {
|
|||||||
} else {
|
} else {
|
||||||
// 是否有注解,有就报错,没有放行
|
// 是否有注解,有就报错,没有放行
|
||||||
if (ObjectUtil.isNotNull(apiEncrypt)) {
|
if (ObjectUtil.isNotNull(apiEncrypt)) {
|
||||||
HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class);
|
handlerExceptionResolver.resolveException(
|
||||||
exceptionResolver.resolveException(
|
|
||||||
servletRequest, servletResponse, null,
|
servletRequest, servletResponse, null,
|
||||||
new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN));
|
new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN));
|
||||||
return;
|
return;
|
||||||
@@ -75,7 +79,6 @@ public class CryptoFilter implements Filter {
|
|||||||
ObjectUtil.defaultIfNull(responseWrapper, response));
|
ObjectUtil.defaultIfNull(responseWrapper, response));
|
||||||
|
|
||||||
if (responseFlag) {
|
if (responseFlag) {
|
||||||
servletResponse.reset();
|
|
||||||
// 对原始内容加密
|
// 对原始内容加密
|
||||||
String encryptContent = responseBodyWrapper.getEncryptContent(
|
String encryptContent = responseBodyWrapper.getEncryptContent(
|
||||||
servletResponse, properties.getPublicKey(), properties.getHeaderFlag());
|
servletResponse, properties.getPublicKey(), properties.getHeaderFlag());
|
||||||
@@ -88,10 +91,9 @@ public class CryptoFilter implements Filter {
|
|||||||
* 获取 ApiEncrypt 注解
|
* 获取 ApiEncrypt 注解
|
||||||
*/
|
*/
|
||||||
private ApiEncrypt getApiEncryptAnnotation(HttpServletRequest servletRequest) {
|
private ApiEncrypt getApiEncryptAnnotation(HttpServletRequest servletRequest) {
|
||||||
RequestMappingHandlerMapping handlerMapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
|
|
||||||
// 获取注解
|
// 获取注解
|
||||||
try {
|
try {
|
||||||
HandlerExecutionChain mappingHandler = handlerMapping.getHandler(servletRequest);
|
HandlerExecutionChain mappingHandler = requestMappingHandlerMapping.getHandler(servletRequest);
|
||||||
if (ObjectUtil.isNotNull(mappingHandler)) {
|
if (ObjectUtil.isNotNull(mappingHandler)) {
|
||||||
Object handler = mappingHandler.getHandler();
|
Object handler = mappingHandler.getHandler();
|
||||||
if (ObjectUtil.isNotNull(handler)) {
|
if (ObjectUtil.isNotNull(handler)) {
|
||||||
|
|||||||
+6
-4
@@ -13,6 +13,7 @@ import java.io.BufferedReader;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,7 +42,8 @@ public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BufferedReader getReader() {
|
public BufferedReader getReader() {
|
||||||
return new BufferedReader(new InputStreamReader(getInputStream()));
|
Charset charset = Charset.forName(getCharacterEncoding());
|
||||||
|
return new BufferedReader(new InputStreamReader(getInputStream(), charset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -72,17 +74,17 @@ public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int available() {
|
public int available() {
|
||||||
return body.length;
|
return bais.available();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFinished() {
|
public boolean isFinished() {
|
||||||
return false;
|
return bais.available() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isReady() {
|
public boolean isReady() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+38
-10
@@ -1,6 +1,5 @@
|
|||||||
package org.dromara.common.encrypt.filter;
|
package org.dromara.common.encrypt.filter;
|
||||||
|
|
||||||
import cn.hutool.core.util.RandomUtil;
|
|
||||||
import jakarta.servlet.ServletOutputStream;
|
import jakarta.servlet.ServletOutputStream;
|
||||||
import jakarta.servlet.WriteListener;
|
import jakarta.servlet.WriteListener;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
@@ -8,7 +7,10 @@ import jakarta.servlet.http.HttpServletResponseWrapper;
|
|||||||
import org.dromara.common.encrypt.utils.EncryptUtils;
|
import org.dromara.common.encrypt.utils.EncryptUtils;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加密响应参数包装类
|
* 加密响应参数包装类
|
||||||
@@ -17,19 +19,26 @@ import java.nio.charset.StandardCharsets;
|
|||||||
*/
|
*/
|
||||||
public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
|
public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
|
||||||
|
|
||||||
|
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||||
|
|
||||||
private final ByteArrayOutputStream byteArrayOutputStream;
|
private final ByteArrayOutputStream byteArrayOutputStream;
|
||||||
private final ServletOutputStream servletOutputStream;
|
private final ServletOutputStream servletOutputStream;
|
||||||
private final PrintWriter printWriter;
|
private PrintWriter printWriter;
|
||||||
|
private Charset charset;
|
||||||
|
|
||||||
public EncryptResponseBodyWrapper(HttpServletResponse response) throws IOException {
|
public EncryptResponseBodyWrapper(HttpServletResponse response) throws IOException {
|
||||||
super(response);
|
super(response);
|
||||||
this.byteArrayOutputStream = new ByteArrayOutputStream();
|
this.byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
this.servletOutputStream = this.getOutputStream();
|
this.servletOutputStream = this.getOutputStream();
|
||||||
this.printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream));
|
this.charset = resolveCharset(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PrintWriter getWriter() {
|
public PrintWriter getWriter() {
|
||||||
|
if (printWriter == null) {
|
||||||
|
charset = resolveCharset((HttpServletResponse) getResponse());
|
||||||
|
printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, charset));
|
||||||
|
}
|
||||||
return printWriter;
|
return printWriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,6 +57,11 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
|
|||||||
byteArrayOutputStream.reset();
|
byteArrayOutputStream.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resetBuffer() {
|
||||||
|
byteArrayOutputStream.reset();
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] getResponseData() throws IOException {
|
public byte[] getResponseData() throws IOException {
|
||||||
flushBuffer();
|
flushBuffer();
|
||||||
return byteArrayOutputStream.toByteArray();
|
return byteArrayOutputStream.toByteArray();
|
||||||
@@ -55,7 +69,7 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
|
|||||||
|
|
||||||
public String getContent() throws IOException {
|
public String getContent() throws IOException {
|
||||||
flushBuffer();
|
flushBuffer();
|
||||||
return byteArrayOutputStream.toString();
|
return byteArrayOutputStream.toString(charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,7 +83,7 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
|
|||||||
*/
|
*/
|
||||||
public String getEncryptContent(HttpServletResponse servletResponse, String publicKey, String headerFlag) throws IOException {
|
public String getEncryptContent(HttpServletResponse servletResponse, String publicKey, String headerFlag) throws IOException {
|
||||||
// 生成秘钥
|
// 生成秘钥
|
||||||
String aesPassword = RandomUtil.randomString(32);
|
String aesPassword = generateAesPassword();
|
||||||
// 秘钥使用 Base64 编码
|
// 秘钥使用 Base64 编码
|
||||||
String encryptAes = EncryptUtils.encryptByBase64(aesPassword);
|
String encryptAes = EncryptUtils.encryptByBase64(aesPassword);
|
||||||
// Rsa 公钥加密 Base64 编码
|
// Rsa 公钥加密 Base64 编码
|
||||||
@@ -78,16 +92,16 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
|
|||||||
// 设置响应头
|
// 设置响应头
|
||||||
// vue版本需要设置
|
// vue版本需要设置
|
||||||
servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag);
|
servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag);
|
||||||
servletResponse.setHeader("Access-Control-Allow-Origin", "*");
|
|
||||||
servletResponse.setHeader("Access-Control-Allow-Methods", "*");
|
|
||||||
servletResponse.setHeader(headerFlag, encryptPassword);
|
servletResponse.setHeader(headerFlag, encryptPassword);
|
||||||
servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString());
|
servletResponse.setCharacterEncoding(charset.name());
|
||||||
|
|
||||||
|
|
||||||
// 获取原始内容
|
// 获取原始内容
|
||||||
String originalBody = this.getContent();
|
String originalBody = this.getContent();
|
||||||
// 对内容进行加密
|
// 对内容进行加密
|
||||||
return EncryptUtils.encryptByAes(originalBody, aesPassword);
|
String encryptContent = EncryptUtils.encryptByAes(originalBody, aesPassword);
|
||||||
|
servletResponse.setContentLengthLong(encryptContent.getBytes(charset).length);
|
||||||
|
return encryptContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -95,7 +109,7 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
|
|||||||
return new ServletOutputStream() {
|
return new ServletOutputStream() {
|
||||||
@Override
|
@Override
|
||||||
public boolean isReady() {
|
public boolean isReady() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -120,4 +134,18 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Charset resolveCharset(HttpServletResponse response) {
|
||||||
|
String characterEncoding = response.getCharacterEncoding();
|
||||||
|
if (characterEncoding == null) {
|
||||||
|
return StandardCharsets.UTF_8;
|
||||||
|
}
|
||||||
|
return Charset.forName(characterEncoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateAesPassword() {
|
||||||
|
byte[] bytes = new byte[24];
|
||||||
|
SECURE_RANDOM.nextBytes(bytes);
|
||||||
|
return Base64.getEncoder().encodeToString(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-91
@@ -1,32 +1,18 @@
|
|||||||
package org.dromara.common.encrypt.interceptor;
|
package org.dromara.common.encrypt.interceptor;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
|
||||||
import cn.hutool.core.convert.Convert;
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.ibatis.executor.parameter.ParameterHandler;
|
|
||||||
import org.apache.ibatis.executor.resultset.ResultSetHandler;
|
import org.apache.ibatis.executor.resultset.ResultSetHandler;
|
||||||
import org.apache.ibatis.plugin.*;
|
import org.apache.ibatis.plugin.*;
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.encrypt.core.EncryptedFieldProcessor;
|
||||||
import org.dromara.common.encrypt.annotation.EncryptField;
|
|
||||||
import org.dromara.common.encrypt.core.EncryptContext;
|
|
||||||
import org.dromara.common.encrypt.core.EncryptorManager;
|
|
||||||
import org.dromara.common.encrypt.enums.AlgorithmType;
|
|
||||||
import org.dromara.common.encrypt.enums.EncodeType;
|
|
||||||
import org.dromara.common.encrypt.properties.EncryptorProperties;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.*;
|
import java.util.Properties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 出参解密拦截器
|
* 出参解密拦截器
|
||||||
*
|
*
|
||||||
* @author 老马
|
* @author Lion Li
|
||||||
* @version 4.6.0
|
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
|
||||||
@Intercepts({@Signature(
|
@Intercepts({@Signature(
|
||||||
type = ResultSetHandler.class,
|
type = ResultSetHandler.class,
|
||||||
method = "handleResultSets",
|
method = "handleResultSets",
|
||||||
@@ -35,91 +21,19 @@ import java.util.*;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class MybatisDecryptInterceptor implements Interceptor {
|
public class MybatisDecryptInterceptor implements Interceptor {
|
||||||
|
|
||||||
private final EncryptorManager encryptorManager;
|
private final EncryptedFieldProcessor encryptedFieldProcessor;
|
||||||
private final EncryptorProperties defaultProperties;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object intercept(Invocation invocation) throws Throwable {
|
public Object intercept(Invocation invocation) throws Throwable {
|
||||||
// 开始进行参数解密
|
|
||||||
ResultSetHandler resultSetHandler = (ResultSetHandler) invocation.getTarget();
|
|
||||||
Field parameterHandlerField = resultSetHandler.getClass().getDeclaredField("parameterHandler");
|
|
||||||
parameterHandlerField.setAccessible(true);
|
|
||||||
Object target = parameterHandlerField.get(resultSetHandler);
|
|
||||||
if (target instanceof ParameterHandler parameterHandler) {
|
|
||||||
Object parameterObject = parameterHandler.getParameterObject();
|
|
||||||
if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) {
|
|
||||||
this.decryptHandler(parameterObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取执行mysql执行结果
|
// 获取执行mysql执行结果
|
||||||
Object result = invocation.proceed();
|
Object result = invocation.proceed();
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
this.decryptHandler(result);
|
encryptedFieldProcessor.decrypt(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 解密对象
|
|
||||||
*
|
|
||||||
* @param sourceObject 待加密对象
|
|
||||||
*/
|
|
||||||
private void decryptHandler(Object sourceObject) {
|
|
||||||
if (ObjectUtil.isNull(sourceObject)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (sourceObject instanceof Map<?, ?> map) {
|
|
||||||
new HashSet<>(map.values()).forEach(this::decryptHandler);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (sourceObject instanceof List<?> list) {
|
|
||||||
if(CollUtil.isEmpty(list)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 判断第一个元素是否含有注解。如果没有直接返回,提高效率
|
|
||||||
Object firstItem = list.get(0);
|
|
||||||
if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
list.forEach(this::decryptHandler);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错)
|
|
||||||
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
|
|
||||||
if(ObjectUtil.isNull(fields)){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
for (Field field : fields) {
|
|
||||||
field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field));
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("处理解密字段时出错", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 字段值进行加密。通过字段的批注注册新的加密算法
|
|
||||||
*
|
|
||||||
* @param value 待加密的值
|
|
||||||
* @param field 待加密字段
|
|
||||||
* @return 加密后结果
|
|
||||||
*/
|
|
||||||
private String decryptField(String value, Field field) {
|
|
||||||
if (ObjectUtil.isNull(value)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
EncryptField encryptField = field.getAnnotation(EncryptField.class);
|
|
||||||
EncryptContext encryptContext = new EncryptContext();
|
|
||||||
encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
|
|
||||||
encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());
|
|
||||||
encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());
|
|
||||||
encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());
|
|
||||||
encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());
|
|
||||||
return this.encryptorManager.decrypt(value, encryptContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object plugin(Object target) {
|
public Object plugin(Object target) {
|
||||||
return Plugin.wrap(target, this);
|
return Plugin.wrap(target, this);
|
||||||
|
|||||||
+14
-80
@@ -1,31 +1,20 @@
|
|||||||
package org.dromara.common.encrypt.interceptor;
|
package org.dromara.common.encrypt.interceptor;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
|
||||||
import cn.hutool.core.convert.Convert;
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.ibatis.executor.parameter.ParameterHandler;
|
import org.apache.ibatis.executor.parameter.ParameterHandler;
|
||||||
import org.apache.ibatis.plugin.*;
|
import org.apache.ibatis.plugin.*;
|
||||||
import org.dromara.common.core.utils.StringUtils;
|
import org.dromara.common.encrypt.core.EncryptedFieldProcessor;
|
||||||
import org.dromara.common.encrypt.annotation.EncryptField;
|
|
||||||
import org.dromara.common.encrypt.core.EncryptContext;
|
|
||||||
import org.dromara.common.encrypt.core.EncryptorManager;
|
|
||||||
import org.dromara.common.encrypt.enums.AlgorithmType;
|
|
||||||
import org.dromara.common.encrypt.enums.EncodeType;
|
|
||||||
import org.dromara.common.encrypt.properties.EncryptorProperties;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.util.*;
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 入参加密拦截器
|
* 入参加密拦截器
|
||||||
*
|
*
|
||||||
* @author 老马
|
* @author Lion Li
|
||||||
* @version 4.6.0
|
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
|
||||||
@Intercepts({@Signature(
|
@Intercepts({@Signature(
|
||||||
type = ParameterHandler.class,
|
type = ParameterHandler.class,
|
||||||
method = "setParameters",
|
method = "setParameters",
|
||||||
@@ -34,19 +23,25 @@ import java.util.*;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class MybatisEncryptInterceptor implements Interceptor {
|
public class MybatisEncryptInterceptor implements Interceptor {
|
||||||
|
|
||||||
private final EncryptorManager encryptorManager;
|
private final EncryptedFieldProcessor encryptedFieldProcessor;
|
||||||
private final EncryptorProperties defaultProperties;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object intercept(Invocation invocation) throws Throwable {
|
public Object intercept(Invocation invocation) throws Throwable {
|
||||||
|
List<EncryptedFieldProcessor.FieldSnapshot> snapshots = List.of();
|
||||||
Object target = invocation.getTarget();
|
Object target = invocation.getTarget();
|
||||||
if (target instanceof ParameterHandler parameterHandler) {
|
if (target instanceof ParameterHandler parameterHandler) {
|
||||||
Object parameterObject = parameterHandler.getParameterObject();
|
Object parameterObject = parameterHandler.getParameterObject();
|
||||||
if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) {
|
if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) {
|
||||||
this.encryptHandler(parameterObject);
|
snapshots = encryptedFieldProcessor.encrypt(parameterObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return invocation.proceed();
|
||||||
|
} finally {
|
||||||
|
for (EncryptedFieldProcessor.FieldSnapshot snapshot : snapshots) {
|
||||||
|
snapshot.restore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return invocation.proceed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -54,67 +49,6 @@ public class MybatisEncryptInterceptor implements Interceptor {
|
|||||||
return Plugin.wrap(target, this);
|
return Plugin.wrap(target, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 加密对象
|
|
||||||
*
|
|
||||||
* @param sourceObject 待加密对象
|
|
||||||
*/
|
|
||||||
private void encryptHandler(Object sourceObject) {
|
|
||||||
if (ObjectUtil.isNull(sourceObject)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (sourceObject instanceof Map<?, ?> map) {
|
|
||||||
new HashSet<>(map.values()).forEach(this::encryptHandler);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (sourceObject instanceof List<?> list) {
|
|
||||||
if(CollUtil.isEmpty(list)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 判断第一个元素是否含有注解。如果没有直接返回,提高效率
|
|
||||||
Object firstItem = list.get(0);
|
|
||||||
if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
list.forEach(this::encryptHandler);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错)
|
|
||||||
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
|
|
||||||
if(ObjectUtil.isNull(fields)){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
for (Field field : fields) {
|
|
||||||
field.set(sourceObject, this.encryptField(Convert.toStr(field.get(sourceObject)), field));
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("处理加密字段时出错", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 字段值进行加密。通过字段的批注注册新的加密算法
|
|
||||||
*
|
|
||||||
* @param value 待加密的值
|
|
||||||
* @param field 待加密字段
|
|
||||||
* @return 加密后结果
|
|
||||||
*/
|
|
||||||
private String encryptField(String value, Field field) {
|
|
||||||
if (ObjectUtil.isNull(value)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
EncryptField encryptField = field.getAnnotation(EncryptField.class);
|
|
||||||
EncryptContext encryptContext = new EncryptContext();
|
|
||||||
encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
|
|
||||||
encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());
|
|
||||||
encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());
|
|
||||||
encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());
|
|
||||||
encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());
|
|
||||||
return this.encryptorManager.encrypt(value, encryptContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setProperties(Properties properties) {
|
public void setProperties(Properties properties) {
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ public class ApiDecryptProperties {
|
|||||||
/**
|
/**
|
||||||
* 头部标识
|
* 头部标识
|
||||||
*/
|
*/
|
||||||
private String headerFlag;
|
private String headerFlag = "encrypt-key";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 响应加密公钥
|
* 响应加密公钥
|
||||||
|
|||||||
+2
-2
@@ -23,7 +23,7 @@ public class EncryptorProperties {
|
|||||||
/**
|
/**
|
||||||
* 默认算法
|
* 默认算法
|
||||||
*/
|
*/
|
||||||
private AlgorithmType algorithm;
|
private AlgorithmType algorithm = AlgorithmType.BASE64;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 安全秘钥
|
* 安全秘钥
|
||||||
@@ -43,6 +43,6 @@ public class EncryptorProperties {
|
|||||||
/**
|
/**
|
||||||
* 编码方式,base64/hex
|
* 编码方式,base64/hex
|
||||||
*/
|
*/
|
||||||
private EncodeType encode;
|
private EncodeType encode = EncodeType.BASE64;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user