Jelajahi Sumber

修复http请求参数重复获取异常问题;新增请求标识;优化多服务部署feign调用逻辑;

Woody 3 minggu lalu
induk
melakukan
ca1f72caae
18 mengubah file dengan 341 tambahan dan 390 penghapusan
  1. 9 6
      framework-base/src/main/java/com/chelvc/framework/base/config/ApplicationConfigurer.java
  2. 16 5
      framework-base/src/main/java/com/chelvc/framework/base/context/DefaultSessionFactory.java
  3. 32 26
      framework-base/src/main/java/com/chelvc/framework/base/context/LoggingContextHolder.java
  4. 5 0
      framework-base/src/main/java/com/chelvc/framework/base/context/Session.java
  5. 12 3
      framework-base/src/main/java/com/chelvc/framework/base/context/SessionContextHolder.java
  6. 0 29
      framework-base/src/main/java/com/chelvc/framework/base/interceptor/BufferedRequestInterceptor.java
  7. 0 89
      framework-base/src/main/java/com/chelvc/framework/base/interceptor/BufferedRequestWrapper.java
  8. 1 2
      framework-base/src/main/java/com/chelvc/framework/base/interceptor/ControllerAccessInterceptor.java
  9. 2 1
      framework-base/src/main/java/com/chelvc/framework/base/interceptor/GlobalExceptionInterceptor.java
  10. 162 0
      framework-base/src/main/java/com/chelvc/framework/base/interceptor/RequestCachingInterceptor.java
  11. 52 60
      framework-base/src/main/java/com/chelvc/framework/base/util/HttpUtils.java
  12. 11 3
      framework-common/src/main/java/com/chelvc/framework/common/util/StringUtils.java
  13. 0 45
      framework-feign/src/main/java/com/chelvc/framework/feign/config/FeignProperties.java
  14. 32 60
      framework-feign/src/main/java/com/chelvc/framework/feign/interceptor/FeignInvokeInterceptor.java
  15. 0 48
      framework-nacos/src/main/java/com/chelvc/framework/nacos/config/MultipleNacosConfigurer.java
  16. 2 2
      framework-nacos/src/main/java/com/chelvc/framework/nacos/config/NacosConfigurer.java
  17. 1 6
      framework-security/src/main/java/com/chelvc/framework/security/interceptor/SecurityValidateInterceptor.java
  18. 4 5
      framework-security/src/main/java/com/chelvc/framework/security/session/OAuthSessionFactory.java

+ 9 - 6
framework-base/src/main/java/com/chelvc/framework/base/config/ApplicationConfigurer.java

@@ -5,7 +5,7 @@ import javax.servlet.Filter;
 
 import com.chelvc.framework.base.context.DefaultSessionFactory;
 import com.chelvc.framework.base.context.SessionFactory;
-import com.chelvc.framework.base.interceptor.BufferedRequestInterceptor;
+import com.chelvc.framework.base.interceptor.RequestCachingInterceptor;
 import com.chelvc.framework.base.interceptor.ResponseWrapInterceptor;
 import com.chelvc.framework.common.util.JacksonUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
@@ -47,6 +47,14 @@ public class ApplicationConfigurer {
         return new DefaultSessionFactory();
     }
 
+    @Bean
+    public FilterRegistrationBean<RequestCachingInterceptor> requestCachingInterceptor() {
+        FilterRegistrationBean<RequestCachingInterceptor> registration = new FilterRegistrationBean<>();
+        registration.setFilter(new RequestCachingInterceptor());
+        registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
+        return registration;
+    }
+
     @Bean
     @ConditionalOnMissingBean(ResponseWrapInterceptor.class)
     public ResponseWrapInterceptor responseWrapInterceptor(RequestMappingHandlerAdapter adapter) {
@@ -60,11 +68,6 @@ public class ApplicationConfigurer {
         return interceptor;
     }
 
-    @Bean
-    public BufferedRequestInterceptor bufferedRequestInterceptor() {
-        return new BufferedRequestInterceptor();
-    }
-
     @Bean
     public FilterRegistrationBean<Filter> crossDomainAccessRegistration() {
         FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();

+ 16 - 5
framework-base/src/main/java/com/chelvc/framework/base/context/DefaultSessionFactory.java

@@ -23,10 +23,10 @@ import lombok.extern.slf4j.Slf4j;
 @Slf4j
 public class DefaultSessionFactory implements SessionFactory {
     /**
-     * 获取客户端身份标识
+     * 获取主体标识
      *
      * @param request Http请求对象
-     * @return 身份标识
+     * @return 主体标识
      */
     protected Long getId(@NonNull HttpServletRequest request) {
         try {
@@ -37,6 +37,17 @@ public class DefaultSessionFactory implements SessionFactory {
         return null;
     }
 
+    /**
+     * 获取请求标识
+     *
+     * @param request Http请求对象
+     * @return 请求标识
+     */
+    protected String getRid(@NonNull HttpServletRequest request) {
+        String rid = HttpUtils.getRid(request);
+        return StringUtils.isEmpty(rid) ? StringUtils.uuid() : rid;
+    }
+
     /**
      * 获取使用类别
      *
@@ -193,9 +204,9 @@ public class DefaultSessionFactory implements SessionFactory {
 
     @Override
     public Session build(@NonNull HttpServletRequest request) {
-        return Session.builder().id(this.getId(request)).using(this.getUsing(request)).host(this.getHost(request))
-                .scope(this.getScope(request)).mobile(this.getMobile(request)).device(this.getDevice(request))
-                .channel(this.getChannel(request)).platform(this.getPlatform(request))
+        return Session.builder().id(this.getId(request)).rid(this.getRid(request)).using(this.getUsing(request))
+                .host(this.getHost(request)).scope(this.getScope(request)).mobile(this.getMobile(request))
+                .device(this.getDevice(request)).channel(this.getChannel(request)).platform(this.getPlatform(request))
                 .terminal(this.getTerminal(request)).version(this.getVersion(request))
                 .timestamp(this.getTimestamp(request)).registering(this.getRegistering(request))
                 .authorities(Collections.unmodifiableSet(this.getAuthorities(request))).build();

+ 32 - 26
framework-base/src/main/java/com/chelvc/framework/base/context/LoggingContextHolder.java

@@ -55,40 +55,19 @@ public final class LoggingContextHolder {
     /**
      * 获取日志信息
      *
-     * @param request  请求对象
+     * @param endpoint 请求端点信息
      * @param messages 消息内容数组
      * @return 消息内容
      */
-    public static String message(ServletRequest request, @NonNull Object... messages) {
-        String uri = null, method = null;
-        if (request instanceof HttpServletRequest) {
-            uri = ((HttpServletRequest) request).getRequestURI();
-            method = ((HttpServletRequest) request).getMethod();
-        }
-        String parameter = request == null || HttpUtils.isMultipart(request) ? null : HttpUtils.serialize(request);
-        return message(uri, method, parameter, messages);
-    }
-
-    /**
-     * 获取日志信息
-     *
-     * @param uri       资源地址
-     * @param method    请求方式
-     * @param parameter 请求参数
-     * @param messages  消息内容数组
-     * @return 消息内容
-     */
-    public static String message(String uri, String method, String parameter, @NonNull Object... messages) {
+    public static String message(String endpoint, @NonNull Object... messages) {
         Session session = SessionContextHolder.getSession(false);
         Long id = ObjectUtils.ifNull(session, Session::getId);
+        String rid = ObjectUtils.ifNull(session, Session::getRid);
         String host = ObjectUtils.ifNull(session, Session::getHost);
         String device = ObjectUtils.ifNull(session, Session::getDevice);
         Platform platform = ObjectUtils.ifNull(session, Session::getPlatform);
         Terminal terminal = ObjectUtils.ifNull(session, Session::getTerminal);
         String version = ObjectUtils.ifNull(session, Session::getVersion);
-        parameter = StringUtils.replace(parameter, '\n', ' ');
-        String request = Stream.of(method, uri, parameter).filter(StringUtils::notEmpty)
-                .collect(Collectors.joining(StringUtils.SPACE));
         StringBuilder buffer = new StringBuilder("[");
         if (StringUtils.notEmpty(host)) {
             buffer.append(host);
@@ -114,8 +93,12 @@ public final class LoggingContextHolder {
             buffer.append(id);
         }
         buffer.append("] [");
-        if (StringUtils.notEmpty(request)) {
-            buffer.append(request);
+        if (StringUtils.notEmpty(rid)) {
+            buffer.append(rid);
+        }
+        buffer.append("] [");
+        if (StringUtils.notEmpty(endpoint)) {
+            buffer.append(endpoint);
         }
         buffer.append("]:");
         for (Object message : messages) {
@@ -124,6 +107,29 @@ public final class LoggingContextHolder {
         return buffer.toString();
     }
 
+    /**
+     * 获取日志信息
+     *
+     * @param request  请求对象
+     * @param messages 消息内容数组
+     * @return 消息内容
+     */
+    public static String message(ServletRequest request, @NonNull Object... messages) {
+        String uri = null, method = null;
+        if (request instanceof HttpServletRequest) {
+            uri = ((HttpServletRequest) request).getRequestURI();
+            method = ((HttpServletRequest) request).getMethod();
+        }
+        String form = null, body = null;
+        if (request != null) {
+            form = HttpUtils.serialize(request.getParameterMap());
+            body = StringUtils.replace(HttpUtils.getBody(request), '\n', '\0');
+        }
+        String endpoint = Stream.of(method, uri, form, body).filter(StringUtils::notEmpty)
+                .collect(Collectors.joining(StringUtils.SPACE));
+        return message(endpoint, messages);
+    }
+
     /**
      * 获取日志处理器
      *

+ 5 - 0
framework-base/src/main/java/com/chelvc/framework/base/context/Session.java

@@ -41,6 +41,11 @@ public class Session implements Serializable {
      */
     private Long id;
 
+    /**
+     * 请求标识
+     */
+    private String rid;
+
     /**
      * 使用类别
      */

+ 12 - 3
framework-base/src/main/java/com/chelvc/framework/base/context/SessionContextHolder.java

@@ -41,7 +41,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
 @RequiredArgsConstructor(onConstructor = @__(@Autowired))
 public class SessionContextHolder implements ServletRequestListener {
     /**
-     * 身份标识请求头
+     * 主体标识请求头
      */
     public static final String HEADER_ID = "id";
 
@@ -200,14 +200,23 @@ public class SessionContextHolder implements ServletRequestListener {
     }
 
     /**
-     * 获取用户ID
+     * 获取主体标识
      *
-     * @return 用户ID
+     * @return 主体标识
      */
     public static Long getId() {
         return ObjectUtils.ifNull(getSession(false), Session::getId);
     }
 
+    /**
+     * 获取请求标识
+     *
+     * @return 请求标识
+     */
+    public static String getRid() {
+        return ObjectUtils.ifNull(getSession(false), Session::getRid);
+    }
+
     /**
      * 获取使用类别
      *

+ 0 - 29
framework-base/src/main/java/com/chelvc/framework/base/interceptor/BufferedRequestInterceptor.java

@@ -1,29 +0,0 @@
-package com.chelvc.framework.base.interceptor;
-
-import java.io.IOException;
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
-
-import com.chelvc.framework.base.util.HttpUtils;
-
-/**
- * 请求体缓存处理拦截器实现
- *
- * @author Woody
- * @date 2024/1/30
- */
-public class BufferedRequestInterceptor implements Filter {
-    @Override
-    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
-            throws IOException, ServletException {
-        if (HttpUtils.isMultipart(request)) {
-            chain.doFilter(request, response);
-        } else {
-            chain.doFilter(new BufferedRequestWrapper((HttpServletRequest) request), response);
-        }
-    }
-}

+ 0 - 89
framework-base/src/main/java/com/chelvc/framework/base/interceptor/BufferedRequestWrapper.java

@@ -1,89 +0,0 @@
-package com.chelvc.framework.base.interceptor;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import javax.servlet.ReadListener;
-import javax.servlet.ServletInputStream;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-
-import com.chelvc.framework.base.util.HttpUtils;
-import lombok.NonNull;
-
-/**
- * 可缓存请求体的请求包装器实现
- *
- * @author Woody
- * @date 2024/1/30
- */
-public class BufferedRequestWrapper extends HttpServletRequestWrapper {
-    private final byte[] body;
-
-    public BufferedRequestWrapper(@NonNull HttpServletRequest request) throws IOException {
-        this(request, HttpUtils.getBody(request));
-    }
-
-    public BufferedRequestWrapper(@NonNull HttpServletRequest request, @NonNull byte[] body) {
-        super(request);
-        this.body = body;
-    }
-
-    @Override
-    public BufferedReader getReader() throws IOException {
-        return new BufferedReader(new InputStreamReader(this.getInputStream()));
-    }
-
-    @Override
-    public ServletInputStream getInputStream() throws IOException {
-        return new BufferedServletInputStream(this.body);
-    }
-
-    /**
-     * 可缓存的Servlet输入流实现
-     */
-    public static class BufferedServletInputStream extends ServletInputStream {
-        private final byte[] bytes;
-        private final InputStream stream;
-
-        public BufferedServletInputStream(@NonNull byte[] bytes) {
-            this.stream = new ByteArrayInputStream(this.bytes = bytes);
-        }
-
-        /**
-         * 获取字节数组
-         *
-         * @return 字节数组
-         */
-        public byte[] bytes() {
-            return this.bytes;
-        }
-
-        @Override
-        public int read() throws IOException {
-            return this.stream.read();
-        }
-
-        @Override
-        public int available() throws IOException {
-            return this.stream.available();
-        }
-
-        @Override
-        public boolean isReady() {
-            return true;
-        }
-
-        @Override
-        public boolean isFinished() {
-            return false;
-        }
-
-        @Override
-        public void setReadListener(ReadListener listener) {
-            throw new UnsupportedOperationException();
-        }
-    }
-}

+ 1 - 2
framework-base/src/main/java/com/chelvc/framework/base/interceptor/ControllerAccessInterceptor.java

@@ -11,7 +11,6 @@ import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.reflect.MethodSignature;
 import org.slf4j.Logger;
 import org.springframework.boot.web.servlet.error.ErrorController;
-import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
 import org.springframework.web.bind.annotation.ExceptionHandler;
@@ -23,9 +22,9 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
  * @date 2024/1/30
  */
 @Slf4j
+@Order
 @Aspect
 @Component
-@Order(Ordered.HIGHEST_PRECEDENCE)
 public class ControllerAccessInterceptor {
     /**
      * 获取接口方法Logger实例

+ 2 - 1
framework-base/src/main/java/com/chelvc/framework/base/interceptor/GlobalExceptionInterceptor.java

@@ -241,7 +241,8 @@ public class GlobalExceptionInterceptor extends AbstractErrorController implemen
         String uri = (String) attributes.get("path");
         String method = request.getMethod();
         String message = (String) attributes.get("error");
-        log.warn(LoggingContextHolder.message(uri, method, null, message));
+        String endpoint = method + StringUtils.SPACE + uri;
+        log.warn(LoggingContextHolder.message(endpoint, message));
 
         Result<?> result;
         if (status.is5xxServerError()) {

+ 162 - 0
framework-base/src/main/java/com/chelvc/framework/base/interceptor/RequestCachingInterceptor.java

@@ -0,0 +1,162 @@
+package com.chelvc.framework.base.interceptor;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.ReadListener;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+import com.chelvc.framework.common.util.FileUtils;
+import com.chelvc.framework.common.util.ObjectUtils;
+
+/**
+ * 请求参数缓存拦截器
+ *
+ * @author Woody
+ * @date 2025/6/25
+ */
+public class RequestCachingInterceptor implements Filter {
+    /**
+     * 缓存数据流对象
+     */
+    static class Stream extends ServletInputStream {
+        private final InputStream delegate;
+
+        private Stream(byte[] bytes) {
+            this.delegate = new ByteArrayInputStream(bytes);
+        }
+
+        @Override
+        public int read() throws IOException {
+            return this.delegate.read();
+        }
+
+        @Override
+        public int read(byte[] b, int off, int len) throws IOException {
+            return this.delegate.read(b, off, len);
+        }
+
+        @Override
+        public int available() throws IOException {
+            return this.delegate.available();
+        }
+
+        @Override
+        public boolean isReady() {
+            try {
+                return this.delegate.available() > 0;
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public boolean isFinished() {
+            try {
+                return this.delegate.available() == 0;
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public void setReadListener(ReadListener listener) {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    /**
+     * 请求缓存包装对象
+     */
+    static class Wrapper extends HttpServletRequestWrapper {
+        private final byte[] body;
+        private final String query;
+        private final Map<String, String[]> parameters;
+
+        private Wrapper(HttpServletRequest request) throws IOException {
+            super(request);
+            if (request instanceof Wrapper) {
+                Wrapper wrapper = (Wrapper) request;
+                this.body = wrapper.body;
+                this.query = wrapper.query;
+                this.parameters = wrapper.parameters;
+            } else {
+                try (InputStream input = request.getInputStream()) {
+                    this.body = FileUtils.getBytes(input);
+                }
+                this.query = request.getQueryString();
+                this.parameters = request.getParameterMap();
+            }
+        }
+
+        @Override
+        public String getQueryString() {
+            return this.query;
+        }
+
+        @Override
+        public String getParameter(String name) {
+            String[] values = this.parameters.get(name);
+            return ObjectUtils.isEmpty(values) ? null : values[0];
+        }
+
+        @Override
+        public Map<String, String[]> getParameterMap() {
+            return this.parameters;
+        }
+
+        @Override
+        public Enumeration<String> getParameterNames() {
+            return new Enumeration<String>() {
+                private final Iterator<String> iterator = parameters.keySet().iterator();
+
+                @Override
+                public boolean hasMoreElements() {
+                    return this.iterator.hasNext();
+                }
+
+                @Override
+                public String nextElement() {
+                    return this.iterator.next();
+                }
+            };
+        }
+
+        @Override
+        public String[] getParameterValues(String name) {
+            return this.parameters.get(name);
+        }
+
+        @Override
+        public BufferedReader getReader() throws IOException {
+            return new BufferedReader(new InputStreamReader(this.getInputStream()));
+        }
+
+        @Override
+        public ServletInputStream getInputStream() throws IOException {
+            return new Stream(this.body);
+        }
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+            throws IOException, ServletException {
+        if (request instanceof HttpServletRequest) {
+            chain.doFilter(new Wrapper((HttpServletRequest) request), response);
+        } else {
+            chain.doFilter(request, response);
+        }
+    }
+}

+ 52 - 60
framework-base/src/main/java/com/chelvc/framework/base/util/HttpUtils.java

@@ -13,7 +13,6 @@ import java.nio.charset.StandardCharsets;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Enumeration;
 import java.util.List;
 import java.util.Map;
 import java.util.function.BiFunction;
@@ -24,16 +23,13 @@ import javax.servlet.ServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import com.chelvc.framework.base.interceptor.BufferedRequestWrapper;
 import com.chelvc.framework.common.model.Media;
 import com.chelvc.framework.common.util.FileUtils;
 import com.chelvc.framework.common.util.HostUtils;
 import com.chelvc.framework.common.util.ObjectUtils;
 import com.chelvc.framework.common.util.StringUtils;
-import com.google.common.collect.Maps;
 import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.http.entity.ContentType;
 import org.apache.poi.ss.usermodel.Workbook;
 import org.springframework.http.MediaType;
 import org.springframework.web.multipart.MultipartFile;
@@ -164,6 +160,32 @@ public final class HttpUtils {
                 .distinct().collect(Collectors.toList());
     }
 
+    /**
+     * 获取Body请求参数
+     *
+     * @param request Http请求对象
+     * @return 请求体参数
+     */
+    public static String getBody(@NonNull ServletRequest request) {
+        byte[] bytes;
+        try (InputStream input = request.getInputStream()) {
+            bytes = FileUtils.getBytes(input);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        return new String(bytes);
+    }
+
+    /**
+     * 获取请求标识
+     *
+     * @param request Http请求对象
+     * @return 请求标识
+     */
+    public static String getRid(@NonNull HttpServletRequest request) {
+        return request.getHeader("x-request-id");
+    }
+
     /**
      * 获取请求域名
      *
@@ -216,52 +238,6 @@ public final class HttpUtils {
         return HostUtils.DEFAULT_LOCAL_ADDRESS_IPV6.equals(host) ? HostUtils.LOCAL_ADDRESS : host;
     }
 
-    /**
-     * 判断ContentType是否是multipart/form-data
-     *
-     * @param request 请求对象
-     * @return true/false
-     */
-    public static boolean isMultipart(@NonNull ServletRequest request) {
-        String type = request.getContentType();
-        return type != null && type.contains(ContentType.MULTIPART_FORM_DATA.getMimeType());
-    }
-
-    /**
-     * 获取Body请求参数
-     *
-     * @param request Http请求对象
-     * @return 请求体字节数组
-     * @throws IOException I/O异常
-     */
-    public static byte[] getBody(@NonNull ServletRequest request) throws IOException {
-        try (InputStream input = request.getInputStream()) {
-            if (input instanceof BufferedRequestWrapper.BufferedServletInputStream) {
-                return ((BufferedRequestWrapper.BufferedServletInputStream) input).bytes();
-            }
-            return FileUtils.getBytes(input);
-        }
-    }
-
-    /**
-     * 获取表单请求参数
-     *
-     * @param request Http请求对象
-     * @return 参数键/值映射表
-     */
-    public static Map<String, String[]> getParameters(@NonNull ServletRequest request) {
-        // 获取表单参数
-        Map<String, String[]> parameters = Maps.newHashMap();
-        Enumeration<String> enumeration = request.getParameterNames();
-        while (enumeration.hasMoreElements()) {
-            String name = enumeration.nextElement();
-            if (StringUtils.notEmpty(name)) {
-                parameters.put(name, request.getParameterValues(name));
-            }
-        }
-        return parameters;
-    }
-
     /**
      * 初始化文件下载相应头
      *
@@ -404,6 +380,30 @@ public final class HttpUtils {
         return buffer.length() == 0 ? (key + StringUtils.EQUAL) : buffer.toString();
     }
 
+    /**
+     * Http请求参数序列化
+     *
+     * @param parameters 参数名称/值映射表
+     * @return 请求参数
+     */
+    public static String serialize(@NonNull Map<String, String[]> parameters) {
+        return serialize(parameters, false);
+    }
+
+    /**
+     * Http请求参数序列化
+     *
+     * @param parameters 参数名称/值映射表
+     * @param ordering   参数是否排序
+     * @return 请求参数
+     */
+    public static String serialize(@NonNull Map<String, String[]> parameters, boolean ordering) {
+        if (ordering) {
+            return StringUtils.join(parameters, "&", HttpUtils::serialize, Comparator.naturalOrder());
+        }
+        return StringUtils.join(parameters, "&", (BiFunction<String, String[], String>) HttpUtils::serialize);
+    }
+
     /**
      * Http请求参数序列化
      *
@@ -425,19 +425,11 @@ public final class HttpUtils {
         String type = request.getContentType();
         if (StringUtils.notEmpty(type) && type.contains(MediaType.APPLICATION_JSON_VALUE)) {
             // Body参数
-            try {
-                return new String(getBody(request));
-            } catch (IOException e) {
-                throw new RuntimeException(e);
-            }
+            return getBody(request);
         }
 
         // 表单参数 + URL参数
-        Map<String, String[]> parameters = getParameters(request);
-        if (ordering) {
-            return StringUtils.join(parameters, "&", HttpUtils::serialize, Comparator.naturalOrder());
-        }
-        return StringUtils.join(parameters, "&", (BiFunction<String, String[], String>) HttpUtils::serialize);
+        return serialize(request.getParameterMap(), ordering);
     }
 
     /**

+ 11 - 3
framework-common/src/main/java/com/chelvc/framework/common/util/StringUtils.java

@@ -567,18 +567,26 @@ public final class StringUtils {
             return source;
         }
 
+        int n = 0;
         char[] chars = null;
         for (int i = 0, size = source.length(); i < size; i++) {
             char original = source.charAt(i);
             char replacement = function.apply(original);
             if (original != replacement) {
                 if (chars == null) {
-                    chars = source.toCharArray();
+                    chars = new char[size];
+                    for (int j = 0; j < i; j++) {
+                        chars[n++] = source.charAt(j);
+                    }
                 }
-                chars[i] = replacement;
+                if (replacement != '\0') {
+                    chars[n++] = replacement;
+                }
+            } else if (Objects.nonNull(chars)) {
+                chars[n++] = original;
             }
         }
-        return chars == null ? source : new String(chars);
+        return chars == null ? source : new String(chars, 0, n);
     }
 
     /**

+ 0 - 45
framework-feign/src/main/java/com/chelvc/framework/feign/config/FeignProperties.java

@@ -1,45 +0,0 @@
-package com.chelvc.framework.feign.config;
-
-import java.util.Collections;
-import java.util.List;
-
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * Feign配置属性
- *
- * @author Woody
- * @date 2024/10/29
- */
-@Data
-@Configuration
-@ConfigurationProperties("feign")
-public class FeignProperties {
-    /**
-     * 请求转发配置
-     */
-    private List<Forward> forwards = Collections.emptyList();
-
-    /**
-     * 请求转发配置
-     */
-    @Data
-    public static class Forward {
-        /**
-         * 原服务
-         */
-        private String source;
-
-        /**
-         * 目标服务
-         */
-        private String target;
-
-        /**
-         * 是否增加接口前缀
-         */
-        private boolean prefixed = false;
-    }
-}

+ 32 - 60
framework-feign/src/main/java/com/chelvc/framework/feign/interceptor/FeignInvokeInterceptor.java

@@ -1,13 +1,9 @@
 package com.chelvc.framework.feign.interceptor;
 
-import java.net.URI;
 import java.util.Collections;
-import java.util.List;
 import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.function.Predicate;
 
+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.base.context.Using;
@@ -16,12 +12,8 @@ import com.chelvc.framework.base.util.SpringUtils;
 import com.chelvc.framework.common.model.Platform;
 import com.chelvc.framework.common.model.Terminal;
 import com.chelvc.framework.common.util.ObjectUtils;
-import com.chelvc.framework.common.util.StringUtils;
-import com.chelvc.framework.feign.config.FeignProperties;
-import com.google.common.collect.Lists;
 import feign.RequestInterceptor;
 import feign.RequestTemplate;
-import feign.Target;
 import lombok.NonNull;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.ApplicationContext;
@@ -35,59 +27,18 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
  */
 @Slf4j
 public class FeignInvokeInterceptor implements RequestInterceptor {
-    private final Set<String> servers;
+    private final String location;
+    private final Map<String, ?> prefixes;
     private final Map<String, ?> mappings;
-    private final List<FeignProperties.Forward> forwards;
 
     @SuppressWarnings("unchecked")
     public FeignInvokeInterceptor(@NonNull ApplicationContext applicationContext) {
+        this.location = "http://" + ApplicationContextHolder.getApplicationName(applicationContext.getEnvironment());
         RequestMappingHandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
-        Map<String, Predicate<Class<?>>> prefixes = handlerMapping.getPathPrefixes();
-        this.servers = ObjectUtils.isEmpty(prefixes) ? Collections.emptySet() : prefixes.keySet();
+        this.prefixes = ObjectUtils.ifEmpty(handlerMapping.getPathPrefixes(), Collections::emptyMap);
         Object registry = ObjectUtils.getObjectValue(handlerMapping, "mappingRegistry");
-        Map<?, ?> mappings = (Map<?, ?>) ObjectUtils.getObjectValue(registry, "urlLookup");
-        this.mappings = ObjectUtils.isEmpty(mappings) ? Collections.emptyMap() : (Map<String, ?>) mappings;
-
-        // 初始化请求转发配置
-        FeignProperties properties = applicationContext.getBean(FeignProperties.class);
-        List<FeignProperties.Forward> forwards = Lists.newArrayList(properties.getForwards());
-        if (SpringUtils.isMultipleServer()
-                && forwards.stream().noneMatch(forward -> Objects.equals(forward.getSource(), "*"))) {
-            FeignProperties.Forward forward = new FeignProperties.Forward();
-            forward.setSource("*");
-            forward.setPrefixed(true);
-            forwards.add(forward);
-        }
-        this.forwards = ObjectUtils.isEmpty(forwards) ? Collections.emptyList() : forwards;
-    }
-
-    /**
-     * 请求转发
-     *
-     * @param template 请求模版对象实例
-     */
-    protected void forward(@NonNull RequestTemplate template) {
-        Target<?> target = template.feignTarget();
-        String server = target.name();
-        for (FeignProperties.Forward forward : this.forwards) {
-            String source = forward.getSource(), mapping = forward.getTarget();
-            if (ObjectUtils.equals(source, server) || ObjectUtils.equals(source, "*")) {
-                boolean internal = this.servers.contains(server);
-                if (internal || forward.isPrefixed()) {
-                    String uri = HttpUtils.uri(server, template.path());
-                    if (!internal || this.mappings.containsKey(uri)) {
-                        template.uri(uri);
-                    }
-                }
-                if (StringUtils.notEmpty(mapping) && !Objects.equals(mapping, server)) {
-                    URI uri = URI.create(target.url());
-                    String url = uri.getScheme() + "://" + mapping;
-                    template.target(url);
-                    template.feignTarget(new Target.HardCodedTarget<>(target.type(), mapping, url));
-                }
-                break;
-            }
-        }
+        Map<String, ?> mappings = (Map<String, ?>) ObjectUtils.getObjectValue(registry, "urlLookup");
+        this.mappings = ObjectUtils.ifEmpty(mappings, Collections::emptyMap);
     }
 
     /**
@@ -101,6 +52,7 @@ public class FeignInvokeInterceptor implements RequestInterceptor {
             return;
         }
         template.header("x-real-ip", session.getHost());
+        template.header("x-request-id", session.getRid());
         template.header(SessionContextHolder.HEADER_ID, (String) ObjectUtils.ifNull(session.getId(), String::valueOf));
         template.header(SessionContextHolder.HEADER_USING, ObjectUtils.ifNull(session.getUsing(), Using::name));
         template.header(SessionContextHolder.HEADER_SCOPE, session.getScope());
@@ -119,13 +71,33 @@ public class FeignInvokeInterceptor implements RequestInterceptor {
         template.header(SessionContextHolder.HEADER_AUTHORITIES, session.getAuthorities());
     }
 
+    /**
+     * 初始化多服务部署接口调用目标
+     *
+     * @param template 请求模版对象实例
+     */
+    protected void initializeMultipleServerTarget(@NonNull RequestTemplate template) {
+        if (!SpringUtils.isMultipleServer()) {
+            return;
+        }
+
+        String server = template.feignTarget().name();
+        if (this.prefixes.containsKey(server)) {
+            String uri = HttpUtils.uri(server, template.path());
+            if (this.mappings.containsKey(uri)) {
+                // 更新接口资源地址
+                template.uri(uri);
+            }
+
+            // 设置本地调用地址
+            template.target(this.location);
+        }
+    }
+
     @Override
     public void apply(RequestTemplate template) {
         this.initializeRequestHeader(template);
-
-        if (ObjectUtils.notEmpty(this.forwards)) {
-            this.forward(template);
-        }
+        this.initializeMultipleServerTarget(template);
 
         if (log.isDebugEnabled()) {
             log.debug("Feign request: {}", template);

+ 0 - 48
framework-nacos/src/main/java/com/chelvc/framework/nacos/config/MultipleNacosConfigurer.java

@@ -1,48 +0,0 @@
-package com.chelvc.framework.nacos.config;
-
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
-import com.alibaba.cloud.nacos.registry.NacosRegistration;
-import com.alibaba.cloud.nacos.registry.NacosServiceRegistry;
-import com.chelvc.framework.base.context.ApplicationContextHolder;
-import com.chelvc.framework.base.util.SpringUtils;
-import com.chelvc.framework.common.util.StringUtils;
-import lombok.RequiredArgsConstructor;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.context.event.ApplicationStartedEvent;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationListener;
-import org.springframework.core.env.Environment;
-import org.springframework.core.io.Resource;
-
-/**
- * 多服务Nacos配置
- * <p>
- * 自动查找工程中所有依赖服务的application.yml配置文件,并将其中的spring.application.name属性值作为服务名称自动注册到Nacos
- *
- * @author Woody
- * @date 2024/1/30
- */
-@RequiredArgsConstructor(onConstructor = @__(@Autowired))
-public class MultipleNacosConfigurer implements ApplicationListener<ApplicationStartedEvent> {
-    private final NacosServiceRegistry registry;
-    private final NacosDiscoveryProperties properties;
-
-    @Override
-    public void onApplicationEvent(ApplicationStartedEvent event) {
-        ApplicationContext applicationContext = event.getApplicationContext();
-        Environment environment = applicationContext.getEnvironment();
-        List<Resource> resources = SpringUtils.getPropertyResources(environment);
-        String application = ApplicationContextHolder.getApplicationName(environment);
-        Set<String> services = resources.stream().map(ApplicationContextHolder::getApplicationName)
-                .filter(service -> StringUtils.notEmpty(service) && !service.equals(application))
-                .collect(Collectors.toSet());
-        services.forEach(service -> {
-            this.properties.setService(service);
-            this.registry.register(new NacosRegistration(null, this.properties, applicationContext));
-        });
-    }
-}

+ 2 - 2
framework-nacos/src/main/java/com/chelvc/framework/nacos/config/NacosConfigConfigurer.java → framework-nacos/src/main/java/com/chelvc/framework/nacos/config/NacosConfigurer.java

@@ -24,7 +24,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
 import org.springframework.core.env.PropertySource;
 
 /**
- * Nacos配置中心配置
+ * Nacos配置
  *
  * @author Woody
  * @date 2024/1/30
@@ -35,7 +35,7 @@ import org.springframework.core.env.PropertySource;
         groupId = "${nacos.config.group:" + Constants.DEFAULT_GROUP + "}", autoRefreshed = true)
 @NacosPropertySource(dataId = "${nacos.config.id:${spring.application.name}}",
         groupId = "${nacos.config.group:" + Constants.DEFAULT_GROUP + "}", autoRefreshed = true)
-public class NacosConfigConfigurer implements InitializingBean, ApplicationListener<NacosConfigReceivedEvent> {
+public class NacosConfigurer implements InitializingBean, ApplicationListener<NacosConfigReceivedEvent> {
     private final ApplicationContext applicationContext;
     private final Map<String, Object> properties = Maps.newHashMap();
 

+ 1 - 6
framework-security/src/main/java/com/chelvc/framework/security/interceptor/SecurityValidateInterceptor.java

@@ -15,7 +15,6 @@ import com.chelvc.framework.base.context.LoggingContextHolder;
 import com.chelvc.framework.base.context.Result;
 import com.chelvc.framework.base.context.Session;
 import com.chelvc.framework.base.context.SessionContextHolder;
-import com.chelvc.framework.base.interceptor.BufferedRequestWrapper;
 import com.chelvc.framework.base.util.HttpUtils;
 import com.chelvc.framework.base.util.SpringUtils;
 import com.chelvc.framework.common.exception.FrameworkException;
@@ -93,11 +92,7 @@ public class SecurityValidateInterceptor implements HandlerInterceptor, WebMvcCo
     private HttpInputMessage decrypt(HttpInputMessage message) throws IOException {
         byte[] bytes;
         try (InputStream input = message.getBody()) {
-            if (input instanceof BufferedRequestWrapper.BufferedServletInputStream) {
-                bytes = ((BufferedRequestWrapper.BufferedServletInputStream) input).bytes();
-            } else {
-                bytes = FileUtils.getBytes(input);
-            }
+            bytes = FileUtils.getBytes(input);
         }
         byte[] ciphertext = CodecUtils.decodeBase64(bytes);
         byte[] plaintext = SecurityContextHolder.getSecurityCipherHandler().decrypt(ciphertext);

+ 4 - 5
framework-security/src/main/java/com/chelvc/framework/security/session/OAuthSessionFactory.java

@@ -20,10 +20,9 @@ import org.springframework.stereotype.Component;
 public class OAuthSessionFactory extends DefaultSessionFactory implements SessionFactory {
     @Override
     public Session build(@NonNull HttpServletRequest request) {
-        return Session.builder().using(Using.NORMAL).host(this.getHost(request))
-                .device(this.getDevice(request)).channel(this.getChannel(request))
-                .platform(this.getPlatform(request)).terminal(this.getTerminal(request))
-                .version(this.getVersion(request)).timestamp(this.getTimestamp(request))
-                .authorities(Collections.emptySet()).build();
+        return Session.builder().rid(this.getRid(request)).using(Using.NORMAL).host(this.getHost(request))
+                .device(this.getDevice(request)).channel(this.getChannel(request)).platform(this.getPlatform(request))
+                .terminal(this.getTerminal(request)).version(this.getVersion(request))
+                .timestamp(this.getTimestamp(request)).authorities(Collections.emptySet()).build();
     }
 }