|
@@ -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;
|
|
|
+ }
|
|
|
+}
|