wangtao 3 anos atrás
pai
commit
8f32819ab4

+ 199 - 0
lb-gateway/src/main/java/com/ydd/gateway/filter/RequestParamGlobalFilter.java

@@ -0,0 +1,199 @@
+package com.ydd.gateway.filter;
+
+import com.ydd.gateway.service.GatewayContext;
+import io.netty.buffer.ByteBufAllocator;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.Ordered;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DataBufferUtils;
+import org.springframework.core.io.buffer.NettyDataBufferFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.http.codec.HttpMessageReader;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
+import org.springframework.stereotype.Component;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.reactive.function.server.HandlerStrategies;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author wangtao
+ * @date 2022/4/1
+ */
+@Component
+public class RequestParamGlobalFilter implements GlobalFilter, Ordered {
+
+    @Override
+    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+
+        /**
+         * save request path and serviceId into gateway context
+         */
+        ServerHttpRequest request = exchange.getRequest();
+        HttpHeaders headers = request.getHeaders();
+
+        // 处理参数
+        MediaType contentType = headers.getContentType();
+        long contentLength = headers.getContentLength();
+
+        if (contentLength > 0) {
+            if (MediaType.APPLICATION_JSON.equals(contentType) || MediaType.APPLICATION_JSON_UTF8.equals(contentType)) {
+                return readBody(exchange, chain);
+            }
+            if (MediaType.APPLICATION_FORM_URLENCODED.equals(contentType) || MediaType.APPLICATION_JSON_UTF8.equals(contentType)) {
+                GatewayContext gatewayContext = new GatewayContext();
+                return readFormData(exchange, chain,gatewayContext);
+            }
+        }
+
+        return chain.filter(exchange);
+    }
+
+
+    /**
+     * default HttpMessageReader
+     */
+    private static final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();
+    /**
+     * ReadJsonBody
+     *
+     * @param exchange
+     * @param chain
+     * @return
+     */
+    private Mono<Void> readBody(ServerWebExchange exchange, GatewayFilterChain chain) {
+        /**
+         * join the body
+         */
+        return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
+            byte[] bytes = new byte[dataBuffer.readableByteCount()];
+            dataBuffer.read(bytes);
+            DataBufferUtils.release(dataBuffer);
+            Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
+                DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
+                DataBufferUtils.retain(buffer);
+                return Mono.just(buffer);
+            });
+            /**
+             * repackage ServerHttpRequest
+             */
+            ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
+                @Override
+                public Flux<DataBuffer> getBody() {
+                    return cachedFlux;
+                }
+            };
+            /**
+             * mutate exchage with new ServerHttpRequest
+             */
+            ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
+            /**
+             * read body string with default messageReaders
+             */
+            return ServerRequest.create(mutatedExchange, messageReaders).bodyToMono(String.class)
+                    .doOnNext(objectValue -> {
+//                        log.debug("[GatewayContext]Read JsonBody:{}", objectValue);
+                    }).then(chain.filter(mutatedExchange));
+        });
+    }
+
+    private Mono<Void> readFormData(ServerWebExchange exchange, GatewayFilterChain chain, GatewayContext gatewayContext){
+        HttpHeaders headers = exchange.getRequest().getHeaders();
+        exchange.getAttributes().put(GatewayContext.CACHE_GATEWAY_CONTEXT,gatewayContext);
+        return exchange.getFormData()
+                .doOnNext(multiValueMap -> {
+                    gatewayContext.setFormData(multiValueMap);
+                })
+                .then(Mono.defer(() -> {
+                    Charset charset = headers.getContentType().getCharset();
+                    charset = charset == null? StandardCharsets.UTF_8:charset;
+                    String charsetName = charset.name();
+                    MultiValueMap<String, String> formData = gatewayContext.getFormData();
+                    /**
+                     * formData is empty just return
+                     */
+                    if(null == formData || formData.isEmpty()){
+                        return chain.filter(exchange);
+                    }
+                    StringBuilder formDataBodyBuilder = new StringBuilder();
+                    String entryKey;
+                    List<String> entryValue;
+                    try {
+                        /**
+                         * repackage form data
+                         */
+                        for (Map.Entry<String, List<String>> entry : formData.entrySet()) {
+                            entryKey = entry.getKey();
+                            entryValue = entry.getValue();
+                            if (entryValue.size() > 1) {
+                                for(String value : entryValue){
+                                    formDataBodyBuilder.append(entryKey).append("=").append(URLEncoder.encode(value, charsetName)).append("&");
+                                }
+                            } else {
+                                formDataBodyBuilder.append(entryKey).append("=").append(URLEncoder.encode(entryValue.get(0), charsetName)).append("&");
+                            }
+                        }
+                    }catch (UnsupportedEncodingException e){
+                        //ignore URLEncode Exception
+                    }
+                    /**
+                     * substring with the last char '&'
+                     */
+                    String formDataBodyString = "";
+                    if(formDataBodyBuilder.length()>0){
+                        formDataBodyString = formDataBodyBuilder.substring(0, formDataBodyBuilder.length() - 1);
+                    }
+                    /**
+                     * get data bytes
+                     */
+                    byte[] bodyBytes =  formDataBodyString.getBytes(charset);
+                    int contentLength = bodyBytes.length;
+                    ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(
+                            exchange.getRequest()) {
+                        /**
+                         * change content-length
+                         * @return
+                         */
+                        @Override
+                        public HttpHeaders getHeaders() {
+                            HttpHeaders httpHeaders = new HttpHeaders();
+                            httpHeaders.putAll(super.getHeaders());
+                            if (contentLength > 0) {
+                                httpHeaders.setContentLength(contentLength);
+                            } else {
+                                httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
+                            }
+                            return httpHeaders;
+                        }
+
+                        /**
+                         * read bytes to Flux<Databuffer>
+                         * @return
+                         */
+                        @Override
+                        public Flux<DataBuffer> getBody() {
+                            return DataBufferUtils.read(new ByteArrayResource(bodyBytes),new NettyDataBufferFactory(ByteBufAllocator.DEFAULT),contentLength);
+                        }
+                    };
+                    ServerWebExchange mutateExchange = exchange.mutate().request(decorator).build();
+                    return chain.filter(mutateExchange);
+                }));
+    }
+    @Override
+    public int getOrder() {
+        return HIGHEST_PRECEDENCE;
+    }
+}