Ver código fonte

加解密逻辑优化

woody 11 meses atrás
pai
commit
df8ccfefc9
31 arquivos alterados com 921 adições e 573 exclusões
  1. 34 38
      framework-base/src/main/java/com/chelvc/framework/base/config/ContextConfigurer.java
  2. 48 0
      framework-base/src/main/java/com/chelvc/framework/base/context/ApplicationContextHolder.java
  3. 0 22
      framework-base/src/main/java/com/chelvc/framework/base/interceptor/DefaultResponseWrapper.java
  4. 42 0
      framework-base/src/main/java/com/chelvc/framework/base/interceptor/ResponseHandleInterceptor.java
  5. 5 6
      framework-base/src/main/java/com/chelvc/framework/base/interceptor/ResponseHandler.java
  6. 55 0
      framework-base/src/main/java/com/chelvc/framework/base/interceptor/ResponseWrapHandler.java
  7. 0 66
      framework-base/src/main/java/com/chelvc/framework/base/interceptor/ResponseWrappingInterceptor.java
  8. 95 0
      framework-common/src/main/java/com/chelvc/framework/common/util/AESUtils.java
  9. 6 7
      framework-database/src/main/java/com/chelvc/framework/database/config/DatabaseConfigurer.java
  10. 5 8
      framework-database/src/main/java/com/chelvc/framework/database/config/MybatisConfigurer.java
  11. 20 221
      framework-database/src/main/java/com/chelvc/framework/database/context/DatabaseContextHolder.java
  12. 107 100
      framework-database/src/main/java/com/chelvc/framework/database/handler/JsonTypeHandler.java
  13. 15 5
      framework-database/src/main/java/com/chelvc/framework/database/handler/SensitiveStringArrayTypeHandler.java
  14. 15 5
      framework-database/src/main/java/com/chelvc/framework/database/handler/SensitiveStringSetTypeHandler.java
  15. 15 5
      framework-database/src/main/java/com/chelvc/framework/database/handler/SensitiveStringsTypeHandler.java
  16. 10 7
      framework-database/src/main/java/com/chelvc/framework/database/handler/SensitiveTypeHandler.java
  17. 2 1
      framework-database/src/main/java/com/chelvc/framework/database/sql/CallableStringDecrypter.java
  18. 2 1
      framework-database/src/main/java/com/chelvc/framework/database/sql/ResultStringDecrypter.java
  19. 4 4
      framework-database/src/main/java/com/chelvc/framework/database/sql/WriteStringEncryptor.java
  20. 3 3
      framework-nacos/src/main/java/com/chelvc/framework/nacos/config/MultiserverNacosConfigurer.java
  21. 43 1
      framework-security/src/main/java/com/chelvc/framework/security/config/SecurityConfigurer.java
  22. 26 0
      framework-security/src/main/java/com/chelvc/framework/security/config/SecurityProperties.java
  23. 147 0
      framework-security/src/main/java/com/chelvc/framework/security/context/SecurityContextHolder.java
  24. 36 53
      framework-security/src/main/java/com/chelvc/framework/security/interceptor/ControllerCryptoInterceptor.java
  25. 2 6
      framework-security/src/main/java/com/chelvc/framework/security/interceptor/PermissionValidateInterceptor.java
  26. 4 14
      framework-security/src/main/java/com/chelvc/framework/security/interceptor/SecurityValidateInterceptor.java
  27. 64 0
      framework-security/src/main/java/com/chelvc/framework/security/interceptor/SensitiveResponseInterceptor.java
  28. 29 0
      framework-security/src/main/java/com/chelvc/framework/security/serializer/SensitiveArraySerializer.java
  29. 30 0
      framework-security/src/main/java/com/chelvc/framework/security/serializer/SensitiveListSerializer.java
  30. 27 0
      framework-security/src/main/java/com/chelvc/framework/security/serializer/SensitiveSerializer.java
  31. 30 0
      framework-security/src/main/java/com/chelvc/framework/security/serializer/SensitiveSetSerializer.java

+ 34 - 38
framework-base/src/main/java/com/chelvc/framework/base/config/ContextConfigurer.java

@@ -1,18 +1,19 @@
 package com.chelvc.framework.base.config;
 
-import java.util.Collections;
 import java.util.List;
-import javax.annotation.PostConstruct;
+import java.util.Map;
 
+import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.DefaultSessionFactory;
 import com.chelvc.framework.base.context.SessionFactory;
-import com.chelvc.framework.base.interceptor.DefaultResponseWrapper;
-import com.chelvc.framework.base.interceptor.ResponseWrapper;
-import com.chelvc.framework.base.interceptor.ResponseWrappingInterceptor;
+import com.chelvc.framework.base.interceptor.ResponseHandleInterceptor;
+import com.chelvc.framework.base.interceptor.ResponseHandler;
+import com.chelvc.framework.common.util.ObjectUtils;
 import com.google.common.collect.Lists;
-import lombok.RequiredArgsConstructor;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.BeansException;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.util.CollectionUtils;
@@ -21,28 +22,38 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
 import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
 
 /**
- * 请求结果统一包装配置
+ * 上下文配置
  *
  * @author Woody
  * @date 2024/1/30
  */
 @Configuration
-@RequiredArgsConstructor(onConstructor = @__(@Autowired))
-public class ContextConfigurer {
-    private final RequestMappingHandlerAdapter handlerAdapter;
-
-    @PostConstruct
-    public void initialize() {
-        ResponseWrappingInterceptor interceptor = this.responseWrappingInterceptor();
-        List<HandlerMethodReturnValueHandler> handlers = this.handlerAdapter.getReturnValueHandlers();
-        if (CollectionUtils.isEmpty(handlers)) {
-            this.handlerAdapter.setReturnValueHandlers(Collections.singletonList(interceptor));
-            return;
+public class ContextConfigurer implements ApplicationContextAware {
+    /**
+     * 初始化响应结果处理拦截器
+     *
+     * @param applicationContext 应用上下文
+     */
+    private void initializeResponseHandleInterceptor(ApplicationContext applicationContext) {
+        RequestMappingHandlerAdapter adapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);
+        List<HandlerMethodReturnValueHandler> valueHandlers = adapter.getReturnValueHandlers();
+        RequestResponseBodyMethodProcessor processor = CollectionUtils.isEmpty(valueHandlers) ? null :
+                valueHandlers.stream().filter(handler -> handler instanceof RequestResponseBodyMethodProcessor)
+                        .map(handler -> (RequestResponseBodyMethodProcessor) handler).findFirst().orElse(null);
+        Map<String, ResponseHandler> beans = applicationContext.getBeansOfType(ResponseHandler.class);
+        List<ResponseHandler> responseHandlers = ApplicationContextHolder.order(beans.values());
+        ResponseHandleInterceptor interceptor = new ResponseHandleInterceptor(processor, responseHandlers);
+        List<HandlerMethodReturnValueHandler> handlers = Lists.newLinkedList();
+        handlers.add(interceptor);
+        if (ObjectUtils.notEmpty(valueHandlers)) {
+            handlers.addAll(valueHandlers);
         }
-        List<HandlerMethodReturnValueHandler> replaces = Lists.newArrayListWithCapacity(handlers.size() + 1);
-        replaces.add(interceptor);
-        replaces.addAll(handlers);
-        this.handlerAdapter.setReturnValueHandlers(replaces);
+        adapter.setReturnValueHandlers(handlers);
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        this.initializeResponseHandleInterceptor(applicationContext);
     }
 
     @Bean
@@ -50,19 +61,4 @@ public class ContextConfigurer {
     public SessionFactory sessionFactory() {
         return new DefaultSessionFactory();
     }
-
-    @Bean
-    @ConditionalOnMissingBean(ResponseWrapper.class)
-    public ResponseWrapper responseWrapper() {
-        return new DefaultResponseWrapper();
-    }
-
-    @Bean
-    public ResponseWrappingInterceptor responseWrappingInterceptor() {
-        List<HandlerMethodReturnValueHandler> handlers = handlerAdapter.getReturnValueHandlers();
-        RequestResponseBodyMethodProcessor processor = CollectionUtils.isEmpty(handlers) ? null :
-                handlers.stream().filter(handler -> handler instanceof RequestResponseBodyMethodProcessor)
-                        .map(handler -> (RequestResponseBodyMethodProcessor) handler).findAny().orElse(null);
-        return new ResponseWrappingInterceptor(this.responseWrapper(), processor);
-    }
 }

+ 48 - 0
framework-base/src/main/java/com/chelvc/framework/base/context/ApplicationContextHolder.java

@@ -7,12 +7,14 @@ import java.lang.reflect.Type;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Properties;
 import java.util.Set;
+import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
@@ -30,6 +32,7 @@ import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.aop.framework.AopProxyUtils;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
 import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
@@ -485,6 +488,51 @@ public class ApplicationContextHolder implements ApplicationContextAware, Proper
         return getApplicationContext().getBeansOfType(type);
     }
 
+    /**
+     * 基于@Order注解对类对象排序
+     *
+     * @param classes 类对象列表
+     */
+    public static void order(List<Class<?>> classes) {
+        if (ObjectUtils.notEmpty(classes)) {
+            classes.sort(Comparator.comparingInt(clazz -> ObjectUtils.ifNull(
+                    clazz.getAnnotation(Order.class), Order::value, () -> 0
+            )));
+        }
+    }
+
+    /**
+     * 基于@Order注解对Bean排序
+     *
+     * @param beans Bean对象实例集合
+     * @param <T>   对象类型
+     * @return 排序后对象实例列表
+     */
+    public static <T> List<T> order(Collection<? extends T> beans) {
+        if (ObjectUtils.isEmpty(beans)) {
+            return Collections.emptyList();
+        }
+        List<T> orders = Lists.newArrayListWithCapacity(beans.size());
+        order(beans, orders::add);
+        return orders;
+    }
+
+    /**
+     * 基于@Order注解对Bean排序
+     *
+     * @param beans    Bean对象实例集合
+     * @param consumer Bean对象实例回调函数
+     * @param <T>      对象类型
+     */
+    public static <T> void order(Collection<? extends T> beans, @NonNull Consumer<T> consumer) {
+        if (ObjectUtils.isEmpty(beans)) {
+            return;
+        }
+        beans.stream().sorted(Comparator.comparingInt(bean -> ObjectUtils.ifNull(
+                AopProxyUtils.ultimateTargetClass(bean).getAnnotation(Order.class), Order::value, () -> 0
+        ))).forEach(consumer);
+    }
+
     /**
      * 获取属性值
      *

+ 0 - 22
framework-base/src/main/java/com/chelvc/framework/base/interceptor/DefaultResponseWrapper.java

@@ -1,22 +0,0 @@
-package com.chelvc.framework.base.interceptor;
-
-import com.chelvc.framework.base.context.ApplicationContextHolder;
-import com.chelvc.framework.base.context.Result;
-import org.springframework.core.MethodParameter;
-import org.springframework.web.context.request.NativeWebRequest;
-
-/**
- * 响应结果包装默认实现
- *
- * @author Woody
- * @date 2024/1/30
- */
-public class DefaultResponseWrapper implements ResponseWrapper {
-    @Override
-    public Result<?> wrap(NativeWebRequest request, MethodParameter method, Object value) {
-        if (value instanceof Result) {
-            return (Result<?>) value;
-        }
-        return Result.success(value, ApplicationContextHolder.getMessage("Success"));
-    }
-}

+ 42 - 0
framework-base/src/main/java/com/chelvc/framework/base/interceptor/ResponseHandleInterceptor.java

@@ -0,0 +1,42 @@
+package com.chelvc.framework.base.interceptor;
+
+import java.util.List;
+
+import com.google.common.collect.Lists;
+import lombok.NonNull;
+import org.springframework.core.MethodParameter;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
+import org.springframework.web.method.support.ModelAndViewContainer;
+import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
+
+/**
+ * 响应结果处理拦截器
+ *
+ * @author Woody
+ * @date 2024/1/30
+ */
+public class ResponseHandleInterceptor implements HandlerMethodReturnValueHandler {
+    private final RequestResponseBodyMethodProcessor processor;
+    private final List<ResponseHandler> handlers;
+
+    public ResponseHandleInterceptor(@NonNull RequestResponseBodyMethodProcessor processor,
+                                     @NonNull List<ResponseHandler> handlers) {
+        this.processor = processor;
+        this.handlers = Lists.newArrayList(handlers);
+    }
+
+    @Override
+    public boolean supportsReturnType(MethodParameter method) {
+        return true;
+    }
+
+    @Override
+    public void handleReturnValue(Object value, MethodParameter method, ModelAndViewContainer container,
+                                  NativeWebRequest request) throws Exception {
+        for (ResponseHandler handler : this.handlers) {
+            value = handler.handle(request, method, value);
+        }
+        this.processor.handleReturnValue(value, method, container, request);
+    }
+}

+ 5 - 6
framework-base/src/main/java/com/chelvc/framework/base/interceptor/ResponseWrapper.java → framework-base/src/main/java/com/chelvc/framework/base/interceptor/ResponseHandler.java

@@ -1,23 +1,22 @@
 package com.chelvc.framework.base.interceptor;
 
-import com.chelvc.framework.base.context.Result;
 import org.springframework.core.MethodParameter;
 import org.springframework.web.context.request.NativeWebRequest;
 
 /**
- * 响应结果包装接口
+ * 响应结果处理器接口
  *
  * @author Woody
  * @date 2024/1/30
  */
-public interface ResponseWrapper {
+public interface ResponseHandler {
     /**
-     * 对象包装
+     * 响应结果处理
      *
      * @param request 请求对象
      * @param method  请求方法
      * @param value   请求结果
-     * @return 结果对象实例
+     * @return 处理后结果
      */
-    Result<?> wrap(NativeWebRequest request, MethodParameter method, Object value);
+    Object handle(NativeWebRequest request, MethodParameter method, Object value);
 }

+ 55 - 0
framework-base/src/main/java/com/chelvc/framework/base/interceptor/ResponseWrapHandler.java

@@ -0,0 +1,55 @@
+package com.chelvc.framework.base.interceptor;
+
+import com.chelvc.framework.base.annotation.ResponseWrapping;
+import com.chelvc.framework.base.context.ApplicationContextHolder;
+import com.chelvc.framework.base.context.Result;
+import org.springframework.core.MethodParameter;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.context.request.NativeWebRequest;
+
+/**
+ * 响应结果包装处理器实现
+ *
+ * @author Woody
+ * @date 2024/1/30
+ */
+@Order
+@Component
+public class ResponseWrapHandler implements ResponseHandler {
+    /**
+     * 判断目标方法是否使用@ResponseBody注解
+     *
+     * @param method 目标方法
+     * @return true/false
+     */
+    private boolean isResponseBodyMethod(MethodParameter method) {
+        return method.hasMethodAnnotation(ResponseBody.class)
+                || method.getDeclaringClass().isAnnotationPresent(ResponseBody.class)
+                || method.getDeclaringClass().isAnnotationPresent(RestController.class);
+    }
+
+    /**
+     * 判断目标方法是否使用了@ResponseWrapping注解
+     *
+     * @param method 目标方法
+     * @return true/false
+     */
+    private boolean isResponseWrappingMethod(MethodParameter method) {
+        ResponseWrapping annotation = method.getMethodAnnotation(ResponseWrapping.class);
+        if (annotation == null) {
+            annotation = method.getDeclaringClass().getAnnotation(ResponseWrapping.class);
+        }
+        return annotation != null && annotation.enabled();
+    }
+
+    @Override
+    public Object handle(NativeWebRequest request, MethodParameter method, Object value) {
+        if (!(value instanceof Result) && this.isResponseBodyMethod(method) && this.isResponseWrappingMethod(method)) {
+            return Result.success(value, ApplicationContextHolder.getMessage("Success"));
+        }
+        return value;
+    }
+}

+ 0 - 66
framework-base/src/main/java/com/chelvc/framework/base/interceptor/ResponseWrappingInterceptor.java

@@ -1,66 +0,0 @@
-package com.chelvc.framework.base.interceptor;
-
-import com.chelvc.framework.base.annotation.ResponseWrapping;
-import lombok.NonNull;
-import org.springframework.core.MethodParameter;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.context.request.NativeWebRequest;
-import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
-import org.springframework.web.method.support.ModelAndViewContainer;
-import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
-
-/**
- * 响应结果包装拦截器
- *
- * @author Woody
- * @date 2024/1/30
- */
-public class ResponseWrappingInterceptor implements HandlerMethodReturnValueHandler {
-    private final ResponseWrapper wrapper;
-    private final RequestResponseBodyMethodProcessor processor;
-
-    public ResponseWrappingInterceptor(@NonNull ResponseWrapper wrapper,
-                                       @NonNull RequestResponseBodyMethodProcessor processor) {
-        this.wrapper = wrapper;
-        this.processor = processor;
-    }
-
-    /**
-     * 判断目标方法是否使用@ResponseBody注解
-     *
-     * @param method 目标方法参数对象
-     * @return true/false
-     */
-    private boolean isResponseBodyMethod(MethodParameter method) {
-        return method.hasMethodAnnotation(ResponseBody.class)
-                || method.getDeclaringClass().isAnnotationPresent(ResponseBody.class)
-                || method.getDeclaringClass().isAnnotationPresent(RestController.class);
-    }
-
-    /**
-     * 判断目标方法是否使用了@ResponseWrapping注解
-     *
-     * @param method 目标方法参数对象
-     * @return true/false
-     */
-    private boolean isResponseWrappingMethod(MethodParameter method) {
-        ResponseWrapping annotation = method.getMethodAnnotation(ResponseWrapping.class);
-        if (annotation == null) {
-            annotation = method.getDeclaringClass().getAnnotation(ResponseWrapping.class);
-        }
-        return annotation != null && annotation.enabled();
-    }
-
-    @Override
-    public boolean supportsReturnType(MethodParameter method) {
-        return this.isResponseBodyMethod(method) && this.isResponseWrappingMethod(method);
-    }
-
-    @Override
-    public void handleReturnValue(Object value, MethodParameter method, ModelAndViewContainer container,
-                                  NativeWebRequest request) throws Exception {
-        value = this.wrapper.wrap(request, method, value);
-        this.processor.handleReturnValue(value, method, container, request);
-    }
-}

+ 95 - 0
framework-common/src/main/java/com/chelvc/framework/common/util/AESUtils.java

@@ -5,8 +5,11 @@ import java.security.GeneralSecurityException;
 import java.security.Key;
 import java.security.NoSuchAlgorithmException;
 import java.security.Security;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.function.Supplier;
+import java.util.stream.Collectors;
 import javax.crypto.Cipher;
 import javax.crypto.KeyGenerator;
 import javax.crypto.spec.IvParameterSpec;
@@ -409,4 +412,96 @@ public final class AESUtils {
     public static String decrypt(@NonNull Cipher cipher, byte[] ciphertext) {
         return ciphertext == null ? null : new String(codec(cipher, ciphertext), StandardCharsets.UTF_8);
     }
+
+    /**
+     * 数据加密
+     *
+     * @param cipher     密码处理器
+     * @param plaintexts 明文数组
+     * @return 密文数组
+     */
+    public static String[] encrypt(@NonNull Cipher cipher, String... plaintexts) {
+        if (ObjectUtils.isEmpty(plaintexts)) {
+            return plaintexts;
+        }
+        String[] ciphertexts = new String[plaintexts.length];
+        for (int i = 0; i < plaintexts.length; i++) {
+            ciphertexts[i] = encrypt(cipher, plaintexts[i]);
+        }
+        return ciphertexts;
+    }
+
+    /**
+     * 数据加密
+     *
+     * @param cipher     密码处理器
+     * @param plaintexts 明文集合
+     * @return 密文集合
+     */
+    public static Set<String> encrypt(@NonNull Cipher cipher, Set<String> plaintexts) {
+        if (ObjectUtils.isEmpty(plaintexts)) {
+            return plaintexts;
+        }
+        return plaintexts.stream().map(plaintext -> encrypt(cipher, plaintext)).collect(Collectors.toSet());
+    }
+
+    /**
+     * 数据加密
+     *
+     * @param cipher     密码处理器
+     * @param plaintexts 明文列表
+     * @return 密文列表
+     */
+    public static List<String> encrypt(@NonNull Cipher cipher, List<String> plaintexts) {
+        if (ObjectUtils.isEmpty(plaintexts)) {
+            return plaintexts;
+        }
+        return plaintexts.stream().map(plaintext -> encrypt(cipher, plaintext)).collect(Collectors.toList());
+    }
+
+    /**
+     * 数据解密
+     *
+     * @param cipher      密码处理器
+     * @param ciphertexts 密文数组
+     * @return 明文数组
+     */
+    public static String[] decrypt(@NonNull Cipher cipher, String... ciphertexts) {
+        if (ObjectUtils.isEmpty(ciphertexts)) {
+            return ciphertexts;
+        }
+        String[] plaintexts = new String[ciphertexts.length];
+        for (int i = 0; i < ciphertexts.length; i++) {
+            plaintexts[i] = decrypt(cipher, ciphertexts[i]);
+        }
+        return plaintexts;
+    }
+
+    /**
+     * 数据解密
+     *
+     * @param cipher      密码处理器
+     * @param ciphertexts 密文集合
+     * @return 明文集合
+     */
+    public static Set<String> decrypt(@NonNull Cipher cipher, Set<String> ciphertexts) {
+        if (ObjectUtils.isEmpty(ciphertexts)) {
+            return ciphertexts;
+        }
+        return ciphertexts.stream().map(ciphertext -> decrypt(cipher, ciphertext)).collect(Collectors.toSet());
+    }
+
+    /**
+     * 数据解密
+     *
+     * @param cipher      密码处理器
+     * @param ciphertexts 密文列表
+     * @return 明文列表
+     */
+    public static List<String> decrypt(@NonNull Cipher cipher, List<String> ciphertexts) {
+        if (ObjectUtils.isEmpty(ciphertexts)) {
+            return ciphertexts;
+        }
+        return ciphertexts.stream().map(ciphertext -> decrypt(cipher, ciphertext)).collect(Collectors.toList());
+    }
 }

+ 6 - 7
framework-database/src/main/java/com/chelvc/framework/database/config/DatabaseConfigurer.java

@@ -1,7 +1,6 @@
 package com.chelvc.framework.database.config;
 
-import java.util.Collection;
-import java.util.Comparator;
+import java.util.Map;
 import java.util.function.Supplier;
 
 import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
@@ -11,6 +10,7 @@ import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
 import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
+import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.common.function.Executor;
 import com.chelvc.framework.common.function.Handler;
 import com.chelvc.framework.common.function.Provider;
@@ -39,7 +39,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.core.annotation.Order;
 import org.springframework.transaction.annotation.Transactional;
 
 /**
@@ -111,10 +110,10 @@ public class DatabaseConfigurer {
         MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
 
         // 设置自定义租户隔离拦截器
-        Collection<InnerInterceptor> inners = this.applicationContext.getBeansOfType(InnerInterceptor.class).values();
-        inners.stream().sorted(Comparator.comparingInt(
-                where -> ObjectUtils.ifNull(where.getClass().getAnnotation(Order.class), Order::value, () -> 0)
-        )).forEach(interceptor::addInnerInterceptor);
+        Map<String, InnerInterceptor> inners = this.applicationContext.getBeansOfType(InnerInterceptor.class);
+        if (ObjectUtils.notEmpty(inners)) {
+            ApplicationContextHolder.order(inners.values(), interceptor::addInnerInterceptor);
+        }
 
         // 设置数据环境隔离过滤拦截器
         interceptor.addInnerInterceptor(new TenantLineInnerInterceptor() {

+ 5 - 8
framework-database/src/main/java/com/chelvc/framework/database/config/MybatisConfigurer.java

@@ -2,11 +2,11 @@ package com.chelvc.framework.database.config;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
 
 import com.baomidou.mybatisplus.core.config.GlobalConfig;
 import com.baomidou.mybatisplus.core.metadata.TableInfo;
+import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.util.SpringUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.google.common.collect.Lists;
@@ -18,7 +18,6 @@ import javassist.LoaderClassPath;
 import javassist.NotFoundException;
 import lombok.NonNull;
 import org.apache.ibatis.parsing.XNode;
-import org.springframework.core.annotation.Order;
 
 /**
  * Mybatis配置类
@@ -129,16 +128,14 @@ public abstract class MybatisConfigurer {
                 MybatisConfigurer.class.getPackage().getName(),
                 clazz -> clazz != MybatisConfigurer.class && MybatisConfigurer.class.isAssignableFrom(clazz)
         );
+        if (ObjectUtils.isEmpty(classes)) {
+            return Collections.emptyList();
+        }
 
         // 基于@Order注解对拦截器对象排序
-        classes.sort(Comparator.comparingInt(
-                clazz -> ObjectUtils.ifNull(clazz.getAnnotation(Order.class), Order::value, () -> 0)
-        ));
+        ApplicationContextHolder.order(classes);
 
         // 初始化对象实例
-        if (ObjectUtils.isEmpty(classes)) {
-            return Collections.emptyList();
-        }
         List<MybatisConfigurer> interceptors = Lists.newArrayListWithCapacity(classes.size());
         for (Class<?> clazz : classes) {
             interceptors.add((MybatisConfigurer) clazz.newInstance());

+ 20 - 221
framework-database/src/main/java/com/chelvc/framework/database/context/DatabaseContextHolder.java

@@ -10,7 +10,6 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
@@ -121,14 +120,32 @@ public final class DatabaseContextHolder {
      */
     private static Cipher getCipher(int mode) {
         DatabaseProperties.Sensitive sensitive = getProperties().getSensitive();
-        String secret = AssertUtils.nonempty(sensitive.getSecret(), () -> "Sensitive secret is missing");
-        String iv = AssertUtils.nonempty(sensitive.getIv(), () -> "Sensitive iv is missing");
+        String secret = AssertUtils.nonempty(sensitive.getSecret(), () -> "Cipher secret is missing");
+        String iv = AssertUtils.nonempty(sensitive.getIv(), () -> "Cipher iv is missing");
         return AESUtils.lookupCipher(
                 AESUtils.CBC_PKCS5PADDING + secret, mode,
                 () -> AESUtils.getCipher(AESUtils.CBC_PKCS5PADDING, mode, secret, iv)
         );
     }
 
+    /**
+     * 获取加密处理器
+     *
+     * @return 加密处理器
+     */
+    public static Cipher getEncryptor() {
+        return getCipher(Cipher.ENCRYPT_MODE);
+    }
+
+    /**
+     * 获取解密处理器
+     *
+     * @return 解密处理器
+     */
+    public static Cipher getDecrypter() {
+        return getCipher(Cipher.DECRYPT_MODE);
+    }
+
     /**
      * 判断是否是敏感字段数据混合模式(同时包含明文、密文)
      *
@@ -259,224 +276,6 @@ public final class DatabaseContextHolder {
         return getTransactor().execute(executor);
     }
 
-    /**
-     * 数据加密
-     *
-     * @param plaintext 明文
-     * @return 密文
-     */
-    public static String encrypt(String plaintext) {
-        return encrypt(plaintext, false);
-    }
-
-    /**
-     * 数据加密
-     *
-     * @param plaintext 明文
-     * @param force     是否忽略异常
-     * @return 密文
-     */
-    public static String encrypt(String plaintext, boolean force) {
-        if (plaintext == null) {
-            return null;
-        }
-        try {
-            return AESUtils.encrypt(getCipher(Cipher.ENCRYPT_MODE), plaintext);
-        } catch (Exception e) {
-            if (force) {
-                log.warn("Data encrypt failed: {}, {}", plaintext, e.getMessage());
-            } else {
-                throw e;
-            }
-        }
-        return plaintext;
-    }
-
-    /**
-     * 数据加密
-     *
-     * @param plaintexts 明文数组
-     * @return 密文数组
-     */
-    public static String[] encrypt(String... plaintexts) {
-        return encrypt(plaintexts, false);
-    }
-
-    /**
-     * 数据加密
-     *
-     * @param plaintexts 明文集合
-     * @return 密文集合
-     */
-    public static Set<String> encrypt(Set<String> plaintexts) {
-        return encrypt(plaintexts, false);
-    }
-
-    /**
-     * 数据加密
-     *
-     * @param plaintexts 明文列表
-     * @return 密文列表
-     */
-    public static List<String> encrypt(List<String> plaintexts) {
-        return encrypt(plaintexts, false);
-    }
-
-    /**
-     * 数据加密
-     *
-     * @param plaintexts 明文数组
-     * @param force      是否忽略异常
-     * @return 密文数组
-     */
-    public static String[] encrypt(String[] plaintexts, boolean force) {
-        if (ObjectUtils.isEmpty(plaintexts)) {
-            return plaintexts;
-        }
-        String[] ciphertexts = new String[plaintexts.length];
-        for (int i = 0; i < plaintexts.length; i++) {
-            ciphertexts[i] = encrypt(plaintexts[i], force);
-        }
-        return ciphertexts;
-    }
-
-    /**
-     * 数据加密
-     *
-     * @param plaintexts 明文集合
-     * @param force      是否忽略异常
-     * @return 密文集合
-     */
-    public static Set<String> encrypt(Set<String> plaintexts, boolean force) {
-        if (ObjectUtils.isEmpty(plaintexts)) {
-            return plaintexts;
-        }
-        return plaintexts.stream().map(plaintext -> encrypt(plaintext, force)).collect(Collectors.toSet());
-    }
-
-    /**
-     * 数据加密
-     *
-     * @param plaintexts 明文列表
-     * @param force      是否忽略异常
-     * @return 密文列表
-     */
-    public static List<String> encrypt(List<String> plaintexts, boolean force) {
-        if (ObjectUtils.isEmpty(plaintexts)) {
-            return plaintexts;
-        }
-        return plaintexts.stream().map(plaintext -> encrypt(plaintext, force)).collect(Collectors.toList());
-    }
-
-    /**
-     * 数据解密
-     *
-     * @param ciphertext 密文
-     * @return 明文
-     */
-    public static String decrypt(String ciphertext) {
-        return decrypt(ciphertext, false);
-    }
-
-    /**
-     * 数据解密
-     *
-     * @param ciphertext 密文
-     * @param force      是否忽略异常
-     * @return 明文
-     */
-    public static String decrypt(String ciphertext, boolean force) {
-        if (ciphertext == null) {
-            return null;
-        }
-        try {
-            return AESUtils.decrypt(getCipher(Cipher.DECRYPT_MODE), ciphertext);
-        } catch (Exception e) {
-            if (force) {
-                log.warn("Data decrypt failed: {}, {}", ciphertext, e.getMessage());
-            } else {
-                throw e;
-            }
-        }
-        return ciphertext;
-    }
-
-    /**
-     * 数据解密
-     *
-     * @param ciphertexts 密文数组
-     * @return 明文数组
-     */
-    public static String[] decrypt(String... ciphertexts) {
-        return decrypt(ciphertexts, false);
-    }
-
-    /**
-     * 数据解密
-     *
-     * @param ciphertexts 密文集合
-     * @return 明文列表
-     */
-    public static Set<String> decrypt(Set<String> ciphertexts) {
-        return decrypt(ciphertexts, false);
-    }
-
-    /**
-     * 数据解密
-     *
-     * @param ciphertexts 密文列表
-     * @return 明文列表
-     */
-    public static List<String> decrypt(List<String> ciphertexts) {
-        return decrypt(ciphertexts, false);
-    }
-
-    /**
-     * 数据解密
-     *
-     * @param ciphertexts 密文数组
-     * @param force       是否忽略异常
-     * @return 明文数组
-     */
-    public static String[] decrypt(String[] ciphertexts, boolean force) {
-        if (ObjectUtils.isEmpty(ciphertexts)) {
-            return ciphertexts;
-        }
-        String[] plaintexts = new String[ciphertexts.length];
-        for (int i = 0; i < ciphertexts.length; i++) {
-            plaintexts[i] = decrypt(ciphertexts[i], force);
-        }
-        return plaintexts;
-    }
-
-    /**
-     * 数据解密
-     *
-     * @param ciphertexts 密文集合
-     * @param force       是否忽略异常
-     * @return 明文集合
-     */
-    public static Set<String> decrypt(Set<String> ciphertexts, boolean force) {
-        if (ObjectUtils.isEmpty(ciphertexts)) {
-            return ciphertexts;
-        }
-        return ciphertexts.stream().map(ciphertext -> decrypt(ciphertext, force)).collect(Collectors.toSet());
-    }
-
-    /**
-     * 数据解密
-     *
-     * @param ciphertexts 密文列表
-     * @param force       是否忽略异常
-     * @return 明文列表
-     */
-    public static List<String> decrypt(List<String> ciphertexts, boolean force) {
-        if (ObjectUtils.isEmpty(ciphertexts)) {
-            return ciphertexts;
-        }
-        return ciphertexts.stream().map(ciphertext -> decrypt(ciphertext, force)).collect(Collectors.toList());
-    }
-
     /**
      * 数据更新或忽略
      *

+ 107 - 100
framework-database/src/main/java/com/chelvc/framework/database/handler/JsonTypeHandler.java

@@ -15,14 +15,13 @@ import java.util.List;
 import java.util.Set;
 
 import com.chelvc.framework.common.annotation.Sensitive;
+import com.chelvc.framework.common.util.AESUtils;
 import com.chelvc.framework.common.util.JacksonUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.database.context.DatabaseContextHolder;
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.core.JsonParser;
 import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.Version;
-import com.fasterxml.jackson.databind.AnnotationIntrospector;
 import com.fasterxml.jackson.databind.DeserializationContext;
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.JsonDeserializer;
@@ -30,6 +29,7 @@ import com.fasterxml.jackson.databind.JsonSerializer;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.SerializerProvider;
 import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
 import com.fasterxml.jackson.databind.type.CollectionType;
 import lombok.NonNull;
 import org.apache.ibatis.executor.result.ResultMapException;
@@ -52,50 +52,50 @@ public interface JsonTypeHandler<T> extends TypeHandler<T> {
     class SensitiveSerializer extends JsonSerializer<String> {
         @Override
         public void serialize(String value, JsonGenerator generator, SerializerProvider provider) throws IOException {
-            if (DatabaseContextHolder.isSensitiveWritable()) {
-                value = DatabaseContextHolder.encrypt(value, true);
+            if (DatabaseContextHolder.isSensitiveWritable() && value != null) {
+                value = AESUtils.encrypt(DatabaseContextHolder.getEncryptor(), value);
             }
             generator.writeString(value);
         }
     }
 
     /**
-     * 敏感字符串集合序列化处理器
+     * 敏感字符串数组序列化处理器
      */
-    class SensitiveSetSerializer extends JsonSerializer<Set<String>> {
+    class SensitiveArraySerializer extends JsonSerializer<String[]> {
         @Override
-        public void serialize(Set<String> value, JsonGenerator generator, SerializerProvider provider)
+        public void serialize(String[] value, JsonGenerator generator, SerializerProvider provider)
                 throws IOException {
-            if (DatabaseContextHolder.isSensitiveWritable()) {
-                value = DatabaseContextHolder.encrypt(value, true);
+            if (DatabaseContextHolder.isSensitiveWritable() && ObjectUtils.notEmpty(value)) {
+                value = AESUtils.encrypt(DatabaseContextHolder.getEncryptor(), value);
             }
             generator.writeObject(value);
         }
     }
 
     /**
-     * 敏感字符串列表序列化处理器
+     * 敏感字符串集合序列化处理器
      */
-    class SensitiveListSerializer extends JsonSerializer<List<String>> {
+    class SensitiveSetSerializer extends JsonSerializer<Set<String>> {
         @Override
-        public void serialize(List<String> value, JsonGenerator generator, SerializerProvider provider)
+        public void serialize(Set<String> value, JsonGenerator generator, SerializerProvider provider)
                 throws IOException {
-            if (DatabaseContextHolder.isSensitiveWritable()) {
-                value = DatabaseContextHolder.encrypt(value, true);
+            if (DatabaseContextHolder.isSensitiveWritable() && ObjectUtils.notEmpty(value)) {
+                value = AESUtils.encrypt(DatabaseContextHolder.getEncryptor(), value);
             }
             generator.writeObject(value);
         }
     }
 
     /**
-     * 敏感字符串数组序列化处理器
+     * 敏感字符串列表序列化处理器
      */
-    class SensitiveArraySerializer extends JsonSerializer<String[]> {
+    class SensitiveListSerializer extends JsonSerializer<List<String>> {
         @Override
-        public void serialize(String[] value, JsonGenerator generator, SerializerProvider provider)
+        public void serialize(List<String> value, JsonGenerator generator, SerializerProvider provider)
                 throws IOException {
-            if (DatabaseContextHolder.isSensitiveWritable()) {
-                value = DatabaseContextHolder.encrypt(value, true);
+            if (DatabaseContextHolder.isSensitiveWritable() && ObjectUtils.notEmpty(value)) {
+                value = AESUtils.encrypt(DatabaseContextHolder.getEncryptor(), value);
             }
             generator.writeObject(value);
         }
@@ -108,7 +108,27 @@ public interface JsonTypeHandler<T> extends TypeHandler<T> {
         @Override
         public String deserialize(JsonParser parser, DeserializationContext context)
                 throws IOException, JsonProcessingException {
-            return DatabaseContextHolder.decrypt(parser.getValueAsString(), true);
+            String value = parser.getValueAsString();
+            return value == null ? null : AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), value);
+        }
+    }
+
+    /**
+     * 敏感字符串数组反序列化处理器
+     */
+    class SensitiveArrayDeserializer extends JsonDeserializer<String[]> {
+        private static final com.fasterxml.jackson.core.type.TypeReference<String[]> TYPE =
+                new com.fasterxml.jackson.core.type.TypeReference<String[]>() {
+                };
+
+        @Override
+        public String[] deserialize(JsonParser parser, DeserializationContext context)
+                throws IOException, JsonProcessingException {
+            String[] values = parser.readValueAs(TYPE);
+            if (ObjectUtils.isEmpty(values)) {
+                return values;
+            }
+            return AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), values);
         }
     }
 
@@ -124,7 +144,10 @@ public interface JsonTypeHandler<T> extends TypeHandler<T> {
         public Set<String> deserialize(JsonParser parser, DeserializationContext context)
                 throws IOException, JsonProcessingException {
             Set<String> values = parser.readValueAs(TYPE);
-            return DatabaseContextHolder.decrypt(values, true);
+            if (ObjectUtils.isEmpty(values)) {
+                return values;
+            }
+            return AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), values);
         }
     }
 
@@ -140,97 +163,81 @@ public interface JsonTypeHandler<T> extends TypeHandler<T> {
         public List<String> deserialize(JsonParser parser, DeserializationContext context)
                 throws IOException, JsonProcessingException {
             List<String> values = parser.readValueAs(TYPE);
-            return DatabaseContextHolder.decrypt(values, true);
-        }
-    }
-
-    /**
-     * 敏感字符串数组反序列化处理器
-     */
-    class SensitiveArrayDeserializer extends JsonDeserializer<String[]> {
-        private static final com.fasterxml.jackson.core.type.TypeReference<String[]> TYPE =
-                new com.fasterxml.jackson.core.type.TypeReference<String[]>() {
-                };
-
-        @Override
-        public String[] deserialize(JsonParser parser, DeserializationContext context)
-                throws IOException, JsonProcessingException {
-            String[] values = parser.readValueAs(TYPE);
-            return DatabaseContextHolder.decrypt(values, true);
+            if (ObjectUtils.isEmpty(values)) {
+                return values;
+            }
+            return AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), values);
         }
     }
 
     /**
      * 对象JSON序列化/反序列化处理器实例
      */
-    ObjectMapper mapper = JacksonUtils.initializeSerializer().setAnnotationIntrospector(new AnnotationIntrospector() {
-        @Override
-        public Version version() {
-            return Version.unknownVersion();
-        }
-
-        @Override
-        public Object findSerializer(Annotated am) {
-            if (!am.hasAnnotation(Sensitive.class)) {
-                return null;
-            }
-            JavaType type = am.getType();
-            Class<?> raw = am.getRawType();
-            if (raw == String.class) {
-                return SensitiveSerializer.class;
-            } else if (raw.isArray() && raw.getComponentType() == String.class) {
-                return SensitiveArraySerializer.class;
-            } else if (type instanceof CollectionType && type.getContentType().getRawClass() == String.class) {
-                if (Set.class.isAssignableFrom(type.getRawClass())) {
-                    return SensitiveSetSerializer.class;
+    ObjectMapper mapper = JacksonUtils.initializeSerializer().setAnnotationIntrospector(
+            new JacksonAnnotationIntrospector() {
+                @Override
+                public Object findSerializer(Annotated am) {
+                    if (!am.hasAnnotation(Sensitive.class)) {
+                        return super.findSerializer(am);
+                    }
+                    JavaType type = am.getType();
+                    Class<?> raw = am.getRawType();
+                    if (raw == String.class) {
+                        return SensitiveSerializer.class;
+                    } else if (raw.isArray() && raw.getComponentType() == String.class) {
+                        return SensitiveArraySerializer.class;
+                    } else if (type instanceof CollectionType && type.getContentType().getRawClass() == String.class) {
+                        if (Set.class.isAssignableFrom(type.getRawClass())) {
+                            return SensitiveSetSerializer.class;
+                        }
+                        return SensitiveListSerializer.class;
+                    }
+                    return super.findSerializer(am);
                 }
-                return SensitiveListSerializer.class;
-            }
-            return null;
-        }
 
-        @Override
-        public Object findDeserializer(Annotated am) {
-            if (!am.hasAnnotation(Sensitive.class)) {
-                return null;
-            }
-            AnnotatedElement element = am.getAnnotated();
-            if (element instanceof Method) {
-                Parameter[] parameters = ((Method) element).getParameters();
-                if (ObjectUtils.notEmpty(parameters) && parameters.length > 0) {
-                    Type type = parameters[0].getParameterizedType();
-                    if (type == String.class) {
-                        return SensitiveDeserializer.class;
-                    } else if (type instanceof ParameterizedType) {
-                        Type raw = ((ParameterizedType) type).getRawType();
-                        Type arg = ((ParameterizedType) type).getActualTypeArguments()[0];
-                        if (raw == Set.class && arg == String.class) {
-                            return SensitiveSetDeserializer.class;
-                        } else if (raw == List.class && arg == String.class) {
+                @Override
+                public Object findDeserializer(Annotated am) {
+                    if (!am.hasAnnotation(Sensitive.class)) {
+                        return super.findDeserializer(am);
+                    }
+                    AnnotatedElement element = am.getAnnotated();
+                    if (element instanceof Method) {
+                        Parameter[] parameters = ((Method) element).getParameters();
+                        if (ObjectUtils.notEmpty(parameters) && parameters.length > 0) {
+                            Type type = parameters[0].getParameterizedType();
+                            if (type == String.class) {
+                                return SensitiveDeserializer.class;
+                            } else if (type instanceof ParameterizedType) {
+                                Type raw = ((ParameterizedType) type).getRawType();
+                                Type arg = ((ParameterizedType) type).getActualTypeArguments()[0];
+                                if (raw == Set.class && arg == String.class) {
+                                    return SensitiveSetDeserializer.class;
+                                } else if (raw == List.class && arg == String.class) {
+                                    return SensitiveListDeserializer.class;
+                                }
+                            } else if (type instanceof Class<?> && ((Class<?>) type).isArray()
+                                    && ((Class<?>) type).getComponentType() == String.class) {
+                                return SensitiveArrayDeserializer.class;
+                            }
+                        }
+                    } else {
+                        JavaType type = am.getType();
+                        Class<?> raw = am.getRawType();
+                        if (raw == String.class) {
+                            return SensitiveDeserializer.class;
+                        } else if (raw.isArray() && raw.getComponentType() == String.class) {
+                            return SensitiveArrayDeserializer.class;
+                        } else if (type instanceof CollectionType
+                                && type.getContentType().getRawClass() == String.class) {
+                            if (Set.class.isAssignableFrom(type.getRawClass())) {
+                                return SensitiveSetDeserializer.class;
+                            }
                             return SensitiveListDeserializer.class;
                         }
-                    } else if (type instanceof Class<?> && ((Class<?>) type).isArray()
-                            && ((Class<?>) type).getComponentType() == String.class) {
-                        return SensitiveArrayDeserializer.class;
                     }
+                    return super.findDeserializer(am);
                 }
-            } else {
-                JavaType type = am.getType();
-                Class<?> raw = am.getRawType();
-                if (raw == String.class) {
-                    return SensitiveDeserializer.class;
-                } else if (raw.isArray() && raw.getComponentType() == String.class) {
-                    return SensitiveArrayDeserializer.class;
-                } else if (type instanceof CollectionType && type.getContentType().getRawClass() == String.class) {
-                    if (Set.class.isAssignableFrom(type.getRawClass())) {
-                        return SensitiveSetDeserializer.class;
-                    }
-                    return SensitiveListDeserializer.class;
-                }
-            }
-            return null;
-        }
-    });
+            });
 
     /**
      * 获取对象类型

+ 15 - 5
framework-database/src/main/java/com/chelvc/framework/database/handler/SensitiveStringArrayTypeHandler.java

@@ -5,6 +5,7 @@ import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 
+import com.chelvc.framework.common.util.AESUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.database.context.DatabaseContextHolder;
 import org.apache.ibatis.type.JdbcType;
@@ -19,8 +20,8 @@ public class SensitiveStringArrayTypeHandler extends StringArrayTypeHandler {
     @Override
     public void setNonNullParameter(PreparedStatement ps, int i, String[] parameter, JdbcType jdbcType)
             throws SQLException {
-        if (ObjectUtils.notEmpty(parameter) && DatabaseContextHolder.isSensitiveWritable()) {
-            parameter = DatabaseContextHolder.encrypt(parameter, true);
+        if (DatabaseContextHolder.isSensitiveWritable() && ObjectUtils.notEmpty(parameter)) {
+            parameter = AESUtils.encrypt(DatabaseContextHolder.getEncryptor(), parameter);
         }
         super.setNonNullParameter(ps, i, parameter, jdbcType);
     }
@@ -28,18 +29,27 @@ public class SensitiveStringArrayTypeHandler extends StringArrayTypeHandler {
     @Override
     public String[] getNullableResult(ResultSet rs, String columnName) throws SQLException {
         String[] values = super.getNullableResult(rs, columnName);
-        return ObjectUtils.isEmpty(values) ? values : DatabaseContextHolder.decrypt(values, true);
+        if (ObjectUtils.isEmpty(values)) {
+            return values;
+        }
+        return AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), values);
     }
 
     @Override
     public String[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
         String[] values = super.getNullableResult(rs, columnIndex);
-        return ObjectUtils.isEmpty(values) ? values : DatabaseContextHolder.decrypt(values, true);
+        if (ObjectUtils.isEmpty(values)) {
+            return values;
+        }
+        return AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), values);
     }
 
     @Override
     public String[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
         String[] values = super.getNullableResult(cs, columnIndex);
-        return ObjectUtils.isEmpty(values) ? values : DatabaseContextHolder.decrypt(values, true);
+        if (ObjectUtils.isEmpty(values)) {
+            return values;
+        }
+        return AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), values);
     }
 }

+ 15 - 5
framework-database/src/main/java/com/chelvc/framework/database/handler/SensitiveStringSetTypeHandler.java

@@ -6,6 +6,7 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.Set;
 
+import com.chelvc.framework.common.util.AESUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.database.context.DatabaseContextHolder;
 import org.apache.ibatis.type.JdbcType;
@@ -20,8 +21,8 @@ public class SensitiveStringSetTypeHandler extends StringSetTypeHandler {
     @Override
     public void setNonNullParameter(PreparedStatement ps, int i, Set<String> parameter, JdbcType jdbcType)
             throws SQLException {
-        if (ObjectUtils.notEmpty(parameter) && DatabaseContextHolder.isSensitiveWritable()) {
-            parameter = DatabaseContextHolder.encrypt(parameter, true);
+        if (DatabaseContextHolder.isSensitiveWritable() && ObjectUtils.notEmpty(parameter)) {
+            parameter = AESUtils.encrypt(DatabaseContextHolder.getEncryptor(), parameter);
         }
         super.setNonNullParameter(ps, i, parameter, jdbcType);
     }
@@ -29,18 +30,27 @@ public class SensitiveStringSetTypeHandler extends StringSetTypeHandler {
     @Override
     public Set<String> getNullableResult(ResultSet rs, String columnName) throws SQLException {
         Set<String> values = super.getNullableResult(rs, columnName);
-        return ObjectUtils.isEmpty(values) ? values : DatabaseContextHolder.decrypt(values, true);
+        if (ObjectUtils.isEmpty(values)) {
+            return values;
+        }
+        return AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), values);
     }
 
     @Override
     public Set<String> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
         Set<String> values = super.getNullableResult(rs, columnIndex);
-        return ObjectUtils.isEmpty(values) ? values : DatabaseContextHolder.decrypt(values, true);
+        if (ObjectUtils.isEmpty(values)) {
+            return values;
+        }
+        return AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), values);
     }
 
     @Override
     public Set<String> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
         Set<String> values = super.getNullableResult(cs, columnIndex);
-        return ObjectUtils.isEmpty(values) ? values : DatabaseContextHolder.decrypt(values, true);
+        if (ObjectUtils.isEmpty(values)) {
+            return values;
+        }
+        return AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), values);
     }
 }

+ 15 - 5
framework-database/src/main/java/com/chelvc/framework/database/handler/SensitiveStringsTypeHandler.java

@@ -6,6 +6,7 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.List;
 
+import com.chelvc.framework.common.util.AESUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.database.context.DatabaseContextHolder;
 import org.apache.ibatis.type.JdbcType;
@@ -20,8 +21,8 @@ public class SensitiveStringsTypeHandler extends StringsTypeHandler {
     @Override
     public void setNonNullParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType)
             throws SQLException {
-        if (ObjectUtils.notEmpty(parameter) && DatabaseContextHolder.isSensitiveWritable()) {
-            parameter = DatabaseContextHolder.encrypt(parameter, true);
+        if (DatabaseContextHolder.isSensitiveWritable() && ObjectUtils.notEmpty(parameter)) {
+            parameter = AESUtils.encrypt(DatabaseContextHolder.getEncryptor(), parameter);
         }
         super.setNonNullParameter(ps, i, parameter, jdbcType);
     }
@@ -29,18 +30,27 @@ public class SensitiveStringsTypeHandler extends StringsTypeHandler {
     @Override
     public List<String> getNullableResult(ResultSet rs, String columnName) throws SQLException {
         List<String> values = super.getNullableResult(rs, columnName);
-        return ObjectUtils.isEmpty(values) ? values : DatabaseContextHolder.decrypt(values, true);
+        if (ObjectUtils.isEmpty(values)) {
+            return values;
+        }
+        return AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), values);
     }
 
     @Override
     public List<String> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
         List<String> values = super.getNullableResult(rs, columnIndex);
-        return ObjectUtils.isEmpty(values) ? values : DatabaseContextHolder.decrypt(values, true);
+        if (ObjectUtils.isEmpty(values)) {
+            return values;
+        }
+        return AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), values);
     }
 
     @Override
     public List<String> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
         List<String> values = super.getNullableResult(cs, columnIndex);
-        return ObjectUtils.isEmpty(values) ? values : DatabaseContextHolder.decrypt(values, true);
+        if (ObjectUtils.isEmpty(values)) {
+            return values;
+        }
+        return AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), values);
     }
 }

+ 10 - 7
framework-database/src/main/java/com/chelvc/framework/database/handler/SensitiveTypeHandler.java

@@ -5,6 +5,7 @@ import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 
+import com.chelvc.framework.common.util.AESUtils;
 import com.chelvc.framework.database.context.DatabaseContextHolder;
 import org.apache.ibatis.type.BaseTypeHandler;
 import org.apache.ibatis.type.JdbcType;
@@ -19,25 +20,27 @@ public class SensitiveTypeHandler extends BaseTypeHandler<String> {
     @Override
     public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
             throws SQLException {
-        if (DatabaseContextHolder.isSensitiveWritable()) {
-            ps.setString(i, DatabaseContextHolder.encrypt(parameter, true));
-        } else {
-            ps.setString(i, parameter);
+        if (DatabaseContextHolder.isSensitiveWritable() && parameter != null) {
+            parameter = AESUtils.encrypt(DatabaseContextHolder.getEncryptor(), parameter);
         }
+        ps.setString(i, parameter);
     }
 
     @Override
     public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
-        return DatabaseContextHolder.decrypt(rs.getString(columnName), true);
+        String value = rs.getString(columnName);
+        return value == null ? null : AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), value);
     }
 
     @Override
     public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
-        return DatabaseContextHolder.decrypt(rs.getString(columnIndex), true);
+        String value = rs.getString(columnIndex);
+        return value == null ? null : AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), value);
     }
 
     @Override
     public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
-        return DatabaseContextHolder.decrypt(cs.getString(columnIndex), true);
+        String value = cs.getString(columnIndex);
+        return value == null ? null : AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), value);
     }
 }

+ 2 - 1
framework-database/src/main/java/com/chelvc/framework/database/sql/CallableStringDecrypter.java

@@ -4,6 +4,7 @@ import java.sql.CallableStatement;
 import java.sql.SQLException;
 import java.util.Map;
 
+import com.chelvc.framework.common.util.AESUtils;
 import com.chelvc.framework.database.context.DatabaseContextHolder;
 
 /**
@@ -28,7 +29,7 @@ public class CallableStringDecrypter extends CallableStatementWrapper {
     private <T> T handle(T original) {
         if (original instanceof String) {
             // 字符串数据解密
-            return (T) DatabaseContextHolder.decrypt((String) original, true);
+            return (T) AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), (String) original);
         }
         return original;
     }

+ 2 - 1
framework-database/src/main/java/com/chelvc/framework/database/sql/ResultStringDecrypter.java

@@ -4,6 +4,7 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.Map;
 
+import com.chelvc.framework.common.util.AESUtils;
 import com.chelvc.framework.database.context.DatabaseContextHolder;
 
 /**
@@ -28,7 +29,7 @@ public class ResultStringDecrypter extends ResultSetWrapper {
     private <T> T handle(T original) {
         if (original instanceof String) {
             // 字符串数据解密
-            return (T) DatabaseContextHolder.decrypt((String) original, true);
+            return (T) AESUtils.decrypt(DatabaseContextHolder.getDecrypter(), (String) original);
         }
         return original;
     }

+ 4 - 4
framework-database/src/main/java/com/chelvc/framework/database/sql/WriteStringEncryptor.java

@@ -4,6 +4,7 @@ import java.sql.PreparedStatement;
 import java.sql.SQLException;
 import java.sql.SQLType;
 
+import com.chelvc.framework.common.util.AESUtils;
 import com.chelvc.framework.database.context.DatabaseContextHolder;
 
 /**
@@ -54,10 +55,9 @@ public class WriteStringEncryptor extends PreparedStatementWrapper {
 
     @Override
     public void setString(int parameterIndex, String x) throws SQLException {
-        if (DatabaseContextHolder.isSensitiveWritable()) {
-            super.setString(parameterIndex, DatabaseContextHolder.encrypt(x, true));
-        } else {
-            super.setString(parameterIndex, x);
+        if (DatabaseContextHolder.isSensitiveWritable() && x != null) {
+            x = AESUtils.encrypt(DatabaseContextHolder.getEncryptor(), x);
         }
+        super.setString(parameterIndex, x);
     }
 }

+ 3 - 3
framework-nacos/src/main/java/com/chelvc/framework/nacos/config/MultiserverNacosConfigurer.java

@@ -24,11 +24,11 @@ import org.springframework.core.io.Resource;
 public class MultiserverNacosConfigurer implements ApplicationListener<ApplicationStartedEvent> {
     private final NacosServiceRegistry registry;
     private final NacosDiscoveryProperties properties;
-    private final ApplicationContext applicationContext;
 
     @Override
     public void onApplicationEvent(ApplicationStartedEvent event) {
-        String applicationName = ApplicationContextHolder.getApplicationName(this.applicationContext);
+        ApplicationContext applicationContext = event.getApplicationContext();
+        String applicationName = ApplicationContextHolder.getApplicationName(applicationContext);
         for (Resource resource : ApplicationContextHolder.getApplicationResources()) {
             // 将打包的服务注册到nacos(排除当前服务)
             String service = ApplicationContextHolder.getApplicationName(resource);
@@ -36,7 +36,7 @@ public class MultiserverNacosConfigurer implements ApplicationListener<Applicati
                 continue;
             }
             this.properties.setService(service);
-            this.registry.register(new NacosRegistration(null, this.properties, this.applicationContext));
+            this.registry.register(new NacosRegistration(null, this.properties, applicationContext));
         }
     }
 }

+ 43 - 1
framework-security/src/main/java/com/chelvc/framework/security/config/SecurityConfigurer.java

@@ -1,12 +1,26 @@
 package com.chelvc.framework.security.config;
 
+import java.util.Set;
 import javax.servlet.Filter;
 
 import com.chelvc.framework.base.interceptor.BufferedRequestInterceptor;
+import com.chelvc.framework.common.annotation.Sensitive;
 import com.chelvc.framework.security.interceptor.MethodSecurityExpression;
+import com.chelvc.framework.security.serializer.SensitiveArraySerializer;
+import com.chelvc.framework.security.serializer.SensitiveListSerializer;
+import com.chelvc.framework.security.serializer.SensitiveSerializer;
+import com.chelvc.framework.security.serializer.SensitiveSetSerializer;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.introspect.Annotated;
+import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
+import com.fasterxml.jackson.databind.type.CollectionType;
 import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.beans.BeansException;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.core.Ordered;
@@ -28,7 +42,7 @@ import org.springframework.web.filter.CorsFilter;
  */
 @Configuration
 @EnableGlobalMethodSecurity(prePostEnabled = true)
-public class SecurityConfigurer extends GlobalMethodSecurityConfiguration {
+public class SecurityConfigurer extends GlobalMethodSecurityConfiguration implements ApplicationContextAware {
     @Override
     protected MethodSecurityExpressionHandler createExpressionHandler() {
         return new DefaultMethodSecurityExpressionHandler() {
@@ -46,6 +60,34 @@ public class SecurityConfigurer extends GlobalMethodSecurityConfiguration {
         };
     }
 
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        // 设置敏感数据注解JSON序列化处理器
+        ObjectMapper mapper = applicationContext.getBean(ObjectMapper.class);
+        mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
+            @Override
+            public Object findSerializer(Annotated am) {
+                if (!am.hasAnnotation(Sensitive.class)) {
+                    return super.findSerializer(am);
+                }
+
+                JavaType type = am.getType();
+                Class<?> raw = am.getRawType();
+                if (raw == String.class) {
+                    return SensitiveSerializer.class;
+                } else if (raw.isArray() && raw.getComponentType() == String.class) {
+                    return SensitiveArraySerializer.class;
+                } else if (type instanceof CollectionType && type.getContentType().getRawClass() == String.class) {
+                    if (Set.class.isAssignableFrom(type.getRawClass())) {
+                        return SensitiveSetSerializer.class;
+                    }
+                    return SensitiveListSerializer.class;
+                }
+                return super.findSerializer(am);
+            }
+        });
+    }
+
     @Bean
     public FilterRegistrationBean<Filter> crossDomainAccessRegistration() {
         FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();

+ 26 - 0
framework-security/src/main/java/com/chelvc/framework/security/config/SecurityProperties.java

@@ -28,4 +28,30 @@ public class SecurityProperties {
      * 请求时长(毫秒)
      */
     private long duration = 60 * 1000;
+
+    /**
+     * 密文标记配置
+     */
+    private final Mark mark = new Mark();
+
+    /**
+     * 密文标记信息
+     */
+    @Data
+    public static class Mark {
+        /**
+         * 是否开启密文标记
+         */
+        private boolean enabled;
+
+        /**
+         * 标记前缀
+         */
+        private String prefix = "ENC(";
+
+        /**
+         * 标记后缀
+         */
+        private String suffix = ")";
+    }
 }

+ 147 - 0
framework-security/src/main/java/com/chelvc/framework/security/context/SecurityContextHolder.java

@@ -1,12 +1,20 @@
 package com.chelvc.framework.security.context;
 
+import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
+import javax.crypto.Cipher;
 
 import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.Session;
 import com.chelvc.framework.base.context.SessionContextHolder;
+import com.chelvc.framework.common.util.AESUtils;
+import com.chelvc.framework.common.util.AssertUtils;
+import com.chelvc.framework.common.util.CodecUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
+import com.chelvc.framework.common.util.StringUtils;
+import com.chelvc.framework.security.config.SecurityProperties;
 import lombok.NonNull;
 
 /**
@@ -21,9 +29,148 @@ public final class SecurityContextHolder {
      */
     public static final String DEFAULT_PERMISSION_GROUP = "DEFAULT";
 
+    /**
+     * 配置属性
+     */
+    private static SecurityProperties PROPERTIES;
+
     private SecurityContextHolder() {
     }
 
+    /**
+     * 获取配置属性
+     *
+     * @return 配置属性
+     */
+    private static SecurityProperties getProperties() {
+        if (PROPERTIES == null) {
+            synchronized (SecurityProperties.class) {
+                if (PROPERTIES == null) {
+                    PROPERTIES = ApplicationContextHolder.getBean(SecurityProperties.class);
+                }
+            }
+        }
+        return PROPERTIES;
+    }
+
+    /**
+     * 获取密码处理器
+     *
+     * @param mode 加解密模式
+     * @return 密码处理器实例
+     */
+    private static Cipher getCipher(int mode) {
+        SecurityProperties properties = getProperties();
+        String secret = AssertUtils.nonempty(properties.getSecret(), () -> "Cipher secret is missing");
+        String iv = AssertUtils.nonempty(properties.getIv(), () -> "Cipher iv is missing");
+        return AESUtils.lookupCipher(
+                AESUtils.CBC_PKCS5PADDING + secret, mode,
+                () -> AESUtils.getCipher(AESUtils.CBC_PKCS5PADDING, mode, secret, iv)
+        );
+    }
+
+    /**
+     * 获取加密处理器
+     *
+     * @return 加密处理器
+     */
+    public static Cipher getEncryptor() {
+        return getCipher(Cipher.ENCRYPT_MODE);
+    }
+
+    /**
+     * 获取解密处理器
+     *
+     * @return 解密处理器
+     */
+    public static Cipher getDecrypter() {
+        return getCipher(Cipher.DECRYPT_MODE);
+    }
+
+    /**
+     * 标记敏感信息
+     *
+     * @param ciphertext 敏感信息密文
+     * @return 敏感信息
+     */
+    public static String mark(String ciphertext) {
+        SecurityProperties.Mark mark = getProperties().getMark();
+        if (!mark.isEnabled() || StringUtils.isEmpty(ciphertext)) {
+            return ciphertext;
+        }
+        String prefix = ObjectUtils.ifNull(mark.getPrefix(), StringUtils.EMPTY);
+        String suffix = ObjectUtils.ifNull(mark.getSuffix(), StringUtils.EMPTY);
+        return prefix + ciphertext + suffix;
+    }
+
+    /**
+     * 标记敏感信息
+     *
+     * @param ciphertexts 敏感信息密文数组
+     * @return 敏感信息数组
+     */
+    public static String[] mark(String... ciphertexts) {
+        if (ObjectUtils.isEmpty(ciphertexts)) {
+            return ciphertexts;
+        }
+        String[] marks = new String[ciphertexts.length];
+        for (int i = 0; i < ciphertexts.length; i++) {
+            marks[i] = mark(ciphertexts[i]);
+        }
+        return marks;
+    }
+
+    /**
+     * 标记敏感信息
+     *
+     * @param ciphertexts 敏感信息密文集合
+     * @return 敏感信息集合
+     */
+    public static Set<String> mark(Set<String> ciphertexts) {
+        if (ObjectUtils.isEmpty(ciphertexts)) {
+            return ciphertexts;
+        }
+        return ciphertexts.stream().map(SecurityContextHolder::mark).collect(Collectors.toSet());
+    }
+
+    /**
+     * 标记敏感信息
+     *
+     * @param ciphertexts 敏感信息密文列表
+     * @return 敏感信息列表
+     */
+    public static List<String> mark(List<String> ciphertexts) {
+        if (ObjectUtils.isEmpty(ciphertexts)) {
+            return ciphertexts;
+        }
+        return ciphertexts.stream().map(SecurityContextHolder::mark).collect(Collectors.toList());
+    }
+
+    /**
+     * 数字签名
+     *
+     * @param session 会话信息
+     * @param payload 请求数据
+     * @return 签名信息
+     */
+    public static String sign(@NonNull Session session, String payload) {
+        String secret = AssertUtils.nonempty(getProperties().getSecret(), () -> "secret is missing");
+        String plaintext = secret + session.getPlatform() + session.getTerminal() + session.getVersion() +
+                session.getTimestamp() + payload;
+        return CodecUtils.md5(plaintext);
+    }
+
+    /**
+     * 校验请求时间戳是否有效
+     *
+     * @param timestamp 时间戳
+     * @return true/false
+     */
+    public static boolean validateTimestamp(long timestamp) {
+        long duration = getProperties().getDuration();
+        return duration < 1 || Math.abs(System.currentTimeMillis() - timestamp) <= duration;
+    }
+
     /**
      * 判断当前用户是否拥有任意权限
      *

+ 36 - 53
framework-security/src/main/java/com/chelvc/framework/security/interceptor/ControllerCryptoInterceptor.java

@@ -8,29 +8,25 @@ import javax.crypto.Cipher;
 
 import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.JacksonContextHolder;
-import com.chelvc.framework.base.context.Result;
 import com.chelvc.framework.base.interceptor.BufferedRequestWrapper;
+import com.chelvc.framework.base.interceptor.ResponseHandler;
 import com.chelvc.framework.common.exception.FrameworkException;
 import com.chelvc.framework.common.util.AESUtils;
-import com.chelvc.framework.common.util.AssertUtils;
 import com.chelvc.framework.common.util.FileUtils;
 import com.chelvc.framework.security.annotation.Crypto;
-import com.chelvc.framework.security.config.SecurityProperties;
-import lombok.RequiredArgsConstructor;
+import com.chelvc.framework.security.context.SecurityContextHolder;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.codec.binary.Base64;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.MethodParameter;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpInputMessage;
 import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
 import org.springframework.http.converter.HttpMessageConverter;
-import org.springframework.http.server.ServerHttpRequest;
-import org.springframework.http.server.ServerHttpResponse;
 import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.context.request.NativeWebRequest;
 import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;
-import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
 
 /**
  * 接口参数加解密拦截器
@@ -40,25 +36,8 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
  */
 @Slf4j
 @ControllerAdvice
-@RequiredArgsConstructor(onConstructor = @__(@Autowired))
-public class ControllerCryptoInterceptor extends RequestBodyAdviceAdapter implements ResponseBodyAdvice<Object> {
-    private final SecurityProperties properties;
-
-    /**
-     * 获取密码处理器
-     *
-     * @param mode 加解密模式
-     * @return 密码处理器实例
-     */
-    private Cipher getCipher(int mode) {
-        String secret = AssertUtils.nonempty(this.properties.getSecret(), () -> "Crypto secret is missing");
-        String iv = AssertUtils.nonempty(this.properties.getIv(), () -> "Crypto iv is missing");
-        return AESUtils.lookupCipher(
-                AESUtils.CBC_PKCS5PADDING + secret, mode,
-                () -> AESUtils.getCipher(AESUtils.CBC_PKCS5PADDING, mode, secret, iv)
-        );
-    }
-
+@Order(Ordered.LOWEST_PRECEDENCE - 1)
+public class ControllerCryptoInterceptor extends RequestBodyAdviceAdapter implements ResponseHandler {
     /**
      * 获取目标方法@Crypto注解
      *
@@ -73,6 +52,28 @@ public class ControllerCryptoInterceptor extends RequestBodyAdviceAdapter implem
         return annotation;
     }
 
+    /**
+     * 判断是否是输入加密方法
+     *
+     * @param method 目标方法
+     * @return true/false
+     */
+    private boolean isInputCryptoMethod(MethodParameter method) {
+        Crypto annotation = this.getCryptoAnnotation(method);
+        return annotation != null && annotation.input();
+    }
+
+    /**
+     * 判断是否是输出加密方法
+     *
+     * @param method 目标方法
+     * @return true/false
+     */
+    private boolean isOutputCryptoMethod(MethodParameter method) {
+        Crypto annotation = this.getCryptoAnnotation(method);
+        return annotation != null && annotation.output();
+    }
+
     /**
      * 获取消息密文
      *
@@ -88,22 +89,15 @@ public class ControllerCryptoInterceptor extends RequestBodyAdviceAdapter implem
         return Base64.decodeBase64(FileUtils.getBytes(input));
     }
 
-    @Override
-    public boolean supports(MethodParameter method, Class<? extends HttpMessageConverter<?>> clazz) {
-        Crypto annotation = this.getCryptoAnnotation(method);
-        return annotation != null && annotation.output();
-    }
-
     @Override
     public boolean supports(MethodParameter method, Type type, Class<? extends HttpMessageConverter<?>> clazz) {
-        Crypto annotation = this.getCryptoAnnotation(method);
-        return annotation != null && annotation.input();
+        return this.isInputCryptoMethod(method);
     }
 
     @Override
     public HttpInputMessage beforeBodyRead(HttpInputMessage message, MethodParameter method, Type type,
                                            Class<? extends HttpMessageConverter<?>> clazz) throws IOException {
-        Cipher cipher = this.getCipher(Cipher.DECRYPT_MODE);
+        Cipher cipher = SecurityContextHolder.getDecrypter();
         byte[] plaintext, ciphertext = this.getCiphertext(message);
         try {
             plaintext = AESUtils.codec(cipher, ciphertext);
@@ -126,22 +120,11 @@ public class ControllerCryptoInterceptor extends RequestBodyAdviceAdapter implem
     }
 
     @Override
-    public Object beforeBodyWrite(Object body, MethodParameter method, MediaType media,
-                                  Class<? extends HttpMessageConverter<?>> clazz,
-                                  ServerHttpRequest request, ServerHttpResponse response) {
-        if (body == null) {
-            return null;
-        }
-        if (body instanceof Result) {
-            Result<?> result = (Result<?>) body;
-            if (result.getData() == null || result.isFailure()) {
-                return result;
-            }
-            Cipher cipher = this.getCipher(Cipher.ENCRYPT_MODE);
-            String ciphertext = AESUtils.encrypt(cipher, JacksonContextHolder.serialize(result.getData()));
-            return Result.of(result.getCode(), ciphertext, result.getMessage());
+    public Object handle(NativeWebRequest request, MethodParameter method, Object value) {
+        if (value != null && this.isOutputCryptoMethod(method)) {
+            Cipher cipher = SecurityContextHolder.getEncryptor();
+            value = AESUtils.encrypt(cipher, JacksonContextHolder.serialize(value));
         }
-        Cipher cipher = this.getCipher(Cipher.ENCRYPT_MODE);
-        return AESUtils.encrypt(cipher, JacksonContextHolder.serialize(body));
+        return value;
     }
 }

+ 2 - 6
framework-security/src/main/java/com/chelvc/framework/security/interceptor/PermissionValidateInterceptor.java

@@ -13,9 +13,7 @@ import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.security.annotation.Permission;
 import com.chelvc.framework.security.context.SecurityContextHolder;
 import com.google.common.collect.Sets;
-import lombok.RequiredArgsConstructor;
 import org.springframework.aop.framework.AopProxyUtils;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.context.event.ApplicationStartedEvent;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationListener;
@@ -34,11 +32,8 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  * @date 2024/5/19
  */
 @Component
-@RequiredArgsConstructor(onConstructor = @__(@Autowired))
 public class PermissionValidateInterceptor implements HandlerInterceptor, WebMvcConfigurer,
         ApplicationListener<ApplicationStartedEvent> {
-    private final ApplicationContext applicationContext;
-
     /**
      * 获取接口权限注解实例
      *
@@ -55,7 +50,8 @@ public class PermissionValidateInterceptor implements HandlerInterceptor, WebMvc
     @Override
     public void onApplicationEvent(ApplicationStartedEvent event) {
         // 检查权限标识是否重复
-        List<Object> controllers = ApplicationContextHolder.lookupControllers(this.applicationContext);
+        ApplicationContext applicationContext = event.getApplicationContext();
+        List<Object> controllers = ApplicationContextHolder.lookupControllers(applicationContext);
         if (ObjectUtils.isEmpty(controllers)) {
             return;
         }

+ 4 - 14
framework-security/src/main/java/com/chelvc/framework/security/interceptor/SecurityValidateInterceptor.java

@@ -10,13 +10,10 @@ import com.chelvc.framework.base.context.Session;
 import com.chelvc.framework.base.context.SessionContextHolder;
 import com.chelvc.framework.base.util.HttpUtils;
 import com.chelvc.framework.common.exception.FrameworkException;
-import com.chelvc.framework.common.util.CodecUtils;
 import com.chelvc.framework.common.util.StringUtils;
 import com.chelvc.framework.security.annotation.Security;
-import com.chelvc.framework.security.config.SecurityProperties;
-import lombok.RequiredArgsConstructor;
+import com.chelvc.framework.security.context.SecurityContextHolder;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.Ordered;
 import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Component;
@@ -33,15 +30,12 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  */
 @Slf4j
 @Component
-@RequiredArgsConstructor(onConstructor = @__(@Autowired))
 public class SecurityValidateInterceptor implements HandlerInterceptor, WebMvcConfigurer {
     /**
      * 签名信息请求头
      */
     private static final String HEADER_SIGNATURE = "signature";
 
-    private final SecurityProperties properties;
-
     /**
      * 获取接口安全注解实例
      *
@@ -79,8 +73,7 @@ public class SecurityValidateInterceptor implements HandlerInterceptor, WebMvcCo
             }
 
             // 请求时间戳校验
-            long duration = this.properties.getDuration();
-            if (duration > 0 && Math.abs(System.currentTimeMillis() - session.getTimestamp()) > duration) {
+            if (!SecurityContextHolder.validateTimestamp(session.getTimestamp())) {
                 throw new FrameworkException(HttpStatus.BAD_REQUEST.name(), null,
                         ApplicationContextHolder.getMessage("Time.Deviated"));
             }
@@ -89,12 +82,9 @@ public class SecurityValidateInterceptor implements HandlerInterceptor, WebMvcCo
         // 数字签名校验
         if (annotation.sign()) {
             String payload = HttpUtils.serialize(request, true);
-            String secret = Objects.requireNonNull(this.properties.getSecret());
-            String plaintext = secret + session.getPlatform() + session.getTerminal() + session.getVersion() +
-                    session.getTimestamp() + payload;
-            String ciphertext = CodecUtils.md5(plaintext);
             String signature = request.getHeader(HEADER_SIGNATURE);
-            LoggingContextHolder.debug(log, signature, plaintext, ciphertext, payload);
+            String ciphertext = SecurityContextHolder.sign(session, payload);
+            LoggingContextHolder.debug(log, signature, ciphertext, payload);
             if (!Objects.equals(ciphertext, signature)) {
                 throw new FrameworkException(HttpStatus.BAD_REQUEST.name(), null,
                         ApplicationContextHolder.getMessage("Signature.Invalid"));

+ 64 - 0
framework-security/src/main/java/com/chelvc/framework/security/interceptor/SensitiveResponseInterceptor.java

@@ -0,0 +1,64 @@
+package com.chelvc.framework.security.interceptor;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import javax.crypto.Cipher;
+
+import com.chelvc.framework.base.interceptor.ResponseHandler;
+import com.chelvc.framework.common.annotation.Sensitive;
+import com.chelvc.framework.common.util.AESUtils;
+import com.chelvc.framework.common.util.ObjectUtils;
+import com.chelvc.framework.security.context.SecurityContextHolder;
+import org.springframework.core.MethodParameter;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.NativeWebRequest;
+
+/**
+ * 敏感数据响应拦截器实现
+ *
+ * @author Woody
+ * @date 2024/6/22
+ */
+@Component
+@Order(Ordered.LOWEST_PRECEDENCE - 2)
+public class SensitiveResponseInterceptor implements ResponseHandler {
+    /**
+     * 判断是否是敏感数据方法
+     *
+     * @param method 目标方法
+     * @return true/false
+     */
+    private boolean isSensitiveMethod(MethodParameter method) {
+        return method.hasMethodAnnotation(Sensitive.class);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public Object handle(NativeWebRequest request, MethodParameter method, Object value) {
+        if (value == null || !this.isSensitiveMethod(method)) {
+            return value;
+        }
+        Cipher cipher = SecurityContextHolder.getEncryptor();
+        Type type = Objects.requireNonNull(method.getMethod()).getGenericReturnType();
+        if (type == String.class) {
+            value = SecurityContextHolder.mark(AESUtils.encrypt(cipher, (String) value));
+        } else if (type instanceof ParameterizedType && ObjectUtils.notBlank(value)) {
+            Type raw = ((ParameterizedType) type).getRawType();
+            Type arg = ((ParameterizedType) type).getActualTypeArguments()[0];
+            if (raw == Set.class && arg == String.class) {
+                value = SecurityContextHolder.mark(AESUtils.encrypt(cipher, (Set<String>) value));
+            } else if (raw == List.class && arg == String.class) {
+                value = SecurityContextHolder.mark(AESUtils.encrypt(cipher, (List<String>) value));
+            }
+        } else if (type instanceof Class && ((Class<?>) type).isArray()
+                && ((Class<?>) type).getComponentType() == String.class && ObjectUtils.notBlank(value)) {
+            value = SecurityContextHolder.mark(AESUtils.encrypt(cipher, (String[]) value));
+        }
+        return value;
+    }
+}

+ 29 - 0
framework-security/src/main/java/com/chelvc/framework/security/serializer/SensitiveArraySerializer.java

@@ -0,0 +1,29 @@
+package com.chelvc.framework.security.serializer;
+
+import java.io.IOException;
+import javax.crypto.Cipher;
+
+import com.chelvc.framework.common.util.AESUtils;
+import com.chelvc.framework.common.util.ObjectUtils;
+import com.chelvc.framework.security.context.SecurityContextHolder;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+/**
+ * 敏感字符串数组序列化处理器
+ *
+ * @author Woody
+ * @date 2024/6/21
+ */
+public class SensitiveArraySerializer extends JsonSerializer<String[]> {
+    @Override
+    public void serialize(String[] value, JsonGenerator generator, SerializerProvider provider)
+            throws IOException {
+        if (ObjectUtils.notEmpty(value)) {
+            Cipher cipher = SecurityContextHolder.getEncryptor();
+            value = SecurityContextHolder.mark(AESUtils.encrypt(cipher, value));
+        }
+        generator.writeObject(value);
+    }
+}

+ 30 - 0
framework-security/src/main/java/com/chelvc/framework/security/serializer/SensitiveListSerializer.java

@@ -0,0 +1,30 @@
+package com.chelvc.framework.security.serializer;
+
+import java.io.IOException;
+import java.util.List;
+import javax.crypto.Cipher;
+
+import com.chelvc.framework.common.util.AESUtils;
+import com.chelvc.framework.common.util.ObjectUtils;
+import com.chelvc.framework.security.context.SecurityContextHolder;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+/**
+ * 敏感字符串列表序列化处理器
+ *
+ * @author Woody
+ * @date 2024/6/21
+ */
+public class SensitiveListSerializer extends JsonSerializer<List<String>> {
+    @Override
+    public void serialize(List<String> value, JsonGenerator generator, SerializerProvider provider)
+            throws IOException {
+        if (ObjectUtils.notEmpty(value)) {
+            Cipher cipher = SecurityContextHolder.getEncryptor();
+            value = SecurityContextHolder.mark(AESUtils.encrypt(cipher, value));
+        }
+        generator.writeObject(value);
+    }
+}

+ 27 - 0
framework-security/src/main/java/com/chelvc/framework/security/serializer/SensitiveSerializer.java

@@ -0,0 +1,27 @@
+package com.chelvc.framework.security.serializer;
+
+import java.io.IOException;
+import javax.crypto.Cipher;
+
+import com.chelvc.framework.common.util.AESUtils;
+import com.chelvc.framework.security.context.SecurityContextHolder;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+/**
+ * 敏感字符串序列化处理器
+ *
+ * @author Woody
+ * @date 2024/6/21
+ */
+public class SensitiveSerializer extends JsonSerializer<String> {
+    @Override
+    public void serialize(String value, JsonGenerator generator, SerializerProvider provider) throws IOException {
+        if (value != null) {
+            Cipher cipher = SecurityContextHolder.getEncryptor();
+            value = SecurityContextHolder.mark(AESUtils.encrypt(cipher, value));
+        }
+        generator.writeString(value);
+    }
+}

+ 30 - 0
framework-security/src/main/java/com/chelvc/framework/security/serializer/SensitiveSetSerializer.java

@@ -0,0 +1,30 @@
+package com.chelvc.framework.security.serializer;
+
+import java.io.IOException;
+import java.util.Set;
+import javax.crypto.Cipher;
+
+import com.chelvc.framework.common.util.AESUtils;
+import com.chelvc.framework.common.util.ObjectUtils;
+import com.chelvc.framework.security.context.SecurityContextHolder;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+/**
+ * 敏感字符串集合序列化处理器
+ *
+ * @author Woody
+ * @date 2024/6/21
+ */
+public class SensitiveSetSerializer extends JsonSerializer<Set<String>> {
+    @Override
+    public void serialize(Set<String> value, JsonGenerator generator, SerializerProvider provider)
+            throws IOException {
+        if (ObjectUtils.notEmpty(value)) {
+            Cipher cipher = SecurityContextHolder.getEncryptor();
+            value = SecurityContextHolder.mark(AESUtils.encrypt(cipher, value));
+        }
+        generator.writeObject(value);
+    }
+}