فهرست منبع

优化数据加密逻辑

woody 1 سال پیش
والد
کامیت
1c323a51d0

+ 1 - 1
framework-security/src/main/java/com/chelvc/framework/security/annotation/Encrypt.java

@@ -19,7 +19,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize;
  */
 @Inherited
 @Documented
-@Target(ElementType.FIELD)
+@Target({ElementType.FIELD, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 @JacksonAnnotationsInside
 @JsonSerialize(using = JacksonEncryptSerializer.class)

+ 31 - 0
framework-security/src/main/java/com/chelvc/framework/security/config/SecurityConfigurer.java

@@ -1,12 +1,20 @@
 package com.chelvc.framework.security.config;
 
+import java.lang.reflect.Method;
+import java.util.Objects;
 import javax.servlet.Filter;
 
+import com.chelvc.framework.base.interceptor.StandardUnifiedResponseWrapper;
+import com.chelvc.framework.base.interceptor.UnifiedResponseWrapper;
+import com.chelvc.framework.security.annotation.Encrypt;
+import com.chelvc.framework.security.context.SecurityContextHolder;
+import lombok.extern.slf4j.Slf4j;
 import org.aopalliance.intercept.MethodInvocation;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Primary;
+import org.springframework.core.MethodParameter;
 import org.springframework.core.Ordered;
 import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
 import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
@@ -16,6 +24,7 @@ import org.springframework.security.config.annotation.method.configuration.Globa
 import org.springframework.security.core.Authentication;
 import org.springframework.security.crypto.factory.PasswordEncoderFactories;
 import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.web.context.request.NativeWebRequest;
 import org.springframework.web.cors.CorsConfiguration;
 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
 import org.springframework.web.filter.CorsFilter;
@@ -26,6 +35,7 @@ import org.springframework.web.filter.CorsFilter;
  * @author Woody
  * @date 2023/4/5
  */
+@Slf4j
 @Configuration
 @EnableGlobalMethodSecurity(prePostEnabled = true)
 public class SecurityConfigurer extends GlobalMethodSecurityConfiguration {
@@ -52,6 +62,27 @@ public class SecurityConfigurer extends GlobalMethodSecurityConfiguration {
         return PasswordEncoderFactories.createDelegatingPasswordEncoder();
     }
 
+    @Bean
+    @Primary
+    public UnifiedResponseWrapper unifiedResponseWrapper() {
+        return new StandardUnifiedResponseWrapper() {
+            @Override
+            public Object wrap(NativeWebRequest request, MethodParameter method, Object value) {
+                // 判断方法返回值是否需要加密
+                Method target = Objects.requireNonNull(method.getMethod());
+                if (target.getReturnType() == String.class && target.isAnnotationPresent(Encrypt.class)) {
+                    try {
+                        value = SecurityContextHolder.encrypt((String) value);
+                    } catch (Exception e) {
+                        // 如果加密失败则回退到明文传输
+                        log.error("Data encrypt failed: {}", e.getMessage());
+                    }
+                }
+                return super.wrap(request, method, value);
+            }
+        };
+    }
+
     @Bean
     public FilterRegistrationBean<Filter> crossDomainAccessRegistration() {
         FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();

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

@@ -8,8 +8,11 @@ import java.util.stream.Stream;
 
 import com.chelvc.framework.base.context.ApplicationContextHolder;
 import com.chelvc.framework.base.context.SessionContextHolder;
+import com.chelvc.framework.base.util.AESUtils;
 import com.chelvc.framework.base.util.StringUtils;
+import com.chelvc.framework.security.config.SecurityProperties;
 import com.fasterxml.jackson.core.type.TypeReference;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.util.CollectionUtils;
 
 /**
@@ -20,6 +23,7 @@ import org.springframework.util.CollectionUtils;
  * @author Woody
  * @date 2023/4/5
  */
+@Slf4j
 public class SecurityContextHolder {
     /**
      * 权限属性名称
@@ -38,6 +42,27 @@ public class SecurityContextHolder {
             new TypeReference<Map<String, Set<String>>>() {
             };
 
+    /**
+     * 数据加密
+     *
+     * @param plaintext 数据明文
+     * @return 数据密文
+     */
+    public static String encrypt(String plaintext) {
+        if (StringUtils.isEmpty(plaintext)) {
+            return plaintext;
+        }
+        SecurityProperties properties = ApplicationContextHolder.getBean(SecurityProperties.class);
+        String secret = Objects.requireNonNull(properties.getSecret(), "secret invalid");
+        String device = Objects.requireNonNull(SessionContextHolder.getDevice(), "device invalid");
+        String iv = StringUtils.substring(device, 0, 16);
+        String ciphertext = String.format("ENC(%s)", AESUtils.encode(plaintext, secret, iv));
+        if (log.isDebugEnabled()) {
+            log.debug("Data encrypt: {} -> {}", plaintext, ciphertext);
+        }
+        return ciphertext;
+    }
+
     /**
      * 判断当前用户是否拥有指定资源权限
      *

+ 7 - 13
framework-security/src/main/java/com/chelvc/framework/security/interceptor/JacksonEncryptSerializer.java

@@ -1,14 +1,10 @@
 package com.chelvc.framework.security.interceptor;
 
 import java.io.IOException;
-import java.util.Objects;
 
-import com.chelvc.framework.base.context.ApplicationContextHolder;
-import com.chelvc.framework.base.context.SessionContextHolder;
-import com.chelvc.framework.base.util.AESUtils;
 import com.chelvc.framework.base.util.StringUtils;
 import com.chelvc.framework.security.annotation.Encrypt;
-import com.chelvc.framework.security.config.SecurityProperties;
+import com.chelvc.framework.security.context.SecurityContextHolder;
 import com.fasterxml.jackson.core.JsonGenerator;
 import com.fasterxml.jackson.databind.BeanProperty;
 import com.fasterxml.jackson.databind.JsonSerializer;
@@ -44,15 +40,13 @@ public class JacksonEncryptSerializer extends StdSerializer<String> implements C
         if (this.encrypt == null || StringUtils.isEmpty(plaintext)) {
             generator.writeString(plaintext);
         } else {
-            SecurityProperties properties = ApplicationContextHolder.getBean(SecurityProperties.class);
-            String secret = Objects.requireNonNull(properties.getSecret(), "secret invalid");
-            String device = Objects.requireNonNull(SessionContextHolder.getDevice(), "device invalid");
-            String iv = StringUtils.substring(device, 0, 16);
-            String ciphertext = String.format("ENC(%s)", AESUtils.encode(plaintext, secret, iv));
-            if (log.isDebugEnabled()) {
-                log.debug("Data encrypt: {} -> {}", plaintext, ciphertext);
+            try {
+                generator.writeString(SecurityContextHolder.encrypt(plaintext));
+            } catch (Exception e) {
+                // 如果加密失败则回退到明文传输
+                log.error("Data encrypt failed: {}", e.getMessage());
+                generator.writeString(plaintext);
             }
-            generator.writeString(ciphertext);
         }
     }