Gateway solves the problem of repeated acquisition of body streams, encrypts, decrypts, adds and deletes parameters for application/json, multipart/form-data, x-www-form-urlencoded.

1. CacheRequestFilter global filter solves the problem of repeated acquisition of body stream
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Component
public class CacheRequestFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        if (request.getHeaders().getContentType() == null) {
            return chain.filter(exchange);
        }
        HttpMethod method = request.getMethod();
        if (method == null || method.matches("GET") || method.matches("DELETE")) {
            return chain.filter(exchange);
        }
        //When there is no cache in the body, only this interceptor will be executed. The reason is that the code in fileMap is not executed, so an empty cache needs to be built when it is empty.
        DefaultDataBufferFactory defaultDataBufferFactory = new DefaultDataBufferFactory();
        DefaultDataBuffer defaultDataBuffer = defaultDataBufferFactory.allocateBuffer(0);
        //Construct a new data stream. When the body is empty, construct an empty stream.
        Flux<DataBuffer> bodyDataBuffer = exchange.getRequest().getBody().defaultIfEmpty(defaultDataBuffer);
        return DataBufferUtils.join(bodyDataBuffer)
                .flatMap(dataBuffer -> {
                    DataBufferUtils.retain(dataBuffer);
                    Flux<DataBuffer> cachedFlux = Flux
                            .defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
                    ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
                        @Override
                        public Flux<DataBuffer> getBody() {
                            return cachedFlux;
                        }
                    };
                    //exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, cachedFlux);
                    return chain.filter(exchange.mutate().request(mutatedRequest).build());
                });
    }

    @Override
    public int getOrder() {
        return 0;
    }

}
2. Encryption and decryption types include json/form-data/x-www-form-urlencoded
3. Encrypt and decrypt the parameters in the body and pass the added and deleted parameters downward (AES encryption and decryption is used here)
package com.suntae.gateway.filter;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.suntae.common.domain.ResultException;
import com.suntae.gateway.utils.AESUtils;
import io.netty.buffer.ByteBufAllocator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.URLDecoder;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;

@Slf4j
@Component
public class DeCodeGlobalFilter implements GlobalFilter, Ordered {

    private final static String sk = "aes key";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        HttpMethod method = request.getMethod();
        long contentLength = request.getHeaders().getContentLength();
        if (contentLength > 0) {
            //Decode only for post requests
            if (HttpMethod.POST.equals(method)) {
                return handlePostMethod(exchange, request, chain);
            }
        }
        return chain.filter(exchange);
    }

    private Mono<Void> handlePostMethod(ServerWebExchange exchange, ServerHttpRequest request, GatewayFilterChain chain) {
        MediaType contentType = request.getHeaders().getContentType();
        if (contentType == null) {
            return Mono.error(new ResultException("Unrecognized request body type"));
        }
        StringBuffer paramBuffer = new StringBuffer();
        Flux<DataBuffer> body = exchange.getRequest().getBody();
        body.subscribe(buffer -> {
            //Parse body data
            CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
            //Decrypt here and reinsert it
            paramBuffer.append(charBuffer);
        });
        //Get the data in the last body
        String param = paramBuffer.toString();
        JSONObject JsonData = null;
        if (MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
            JsonData = JSONObject.parseObject(param);
        }
        if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) {
            JsonData = handleFormURLEncoded(param);
        }
        if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) {
            JsonData = JSON.parseObject(handleFormData(param, contentType.toString()));
        }
        if (JsonData == null) {
            return Mono.error(new ResultException("Unsupported request body type"));
        }

        for (String str : JsonData.keySet()) {
            log.info("Parameter:{}", str);
            log.info("{}before decryption:{}", str, JsonData.get(str));
            //Value before decryption
            String enCode = JsonData.getString(str);
            String deCode;
            try {
                deCode = AESUtils.decrypt(URLDecoder.decode(enCode, "UTF-8"), sk);
            } catch (Exception e) {
                throw new ResultException("Decryption failed");
            }
            //Decrypted
            log.info("{}After decryption:{}", str, deCode);
            param = param.replace(enCode, deCode);
        }
        String bodyStr = param;
        DataBuffer bodyDataBuffer = stringBuffer(bodyStr);
        Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
        ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
                exchange.getRequest()) {
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders httpHeaders = new HttpHeaders();
                int length = bodyStr.getBytes().length;
                httpHeaders.putAll(super.getHeaders());
                httpHeaders.remove(HttpHeaders.CONTENT_TYPE);
                httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);
                httpHeaders.setContentLength(length);
                httpHeaders.set(HttpHeaders.CONTENT_TYPE, contentType.toString());
                //Set CONTENT_TYPE
                return httpHeaders;
            }

            @Override
            public Flux<DataBuffer> getBody() {
                return bodyFlux;
            }
        };
        return chain.filter(exchange.mutate().request(mutatedRequest).build());
    }

    private JSONObject handleFormURLEncoded(String param) {


        JSONObject jsonObject = new JSONObject();
        String[] split = param.split(" & amp;");
        for (String s : split) {
            String[] split1 = s.split("=");
            jsonObject.put(split1[0], split1[1]);
        }
        return jsonObject;

    }


    protected DataBuffer stringBuffer(String value) {
        byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
        NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
        DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
        buffer.write(bytes);
        return buffer;
    }

    private String handleFormData(String str, String contentType) {
        String sep = "--" + contentType.replace("multipart/form-data;boundary=", "");
        String[] strs = str.split("\r\\
");
        boolean bankRow = false; // Blank row
        boolean keyRow = true; // name=xxx row
        boolean append = false; // Whether the content is spliced with line breaks
        Map<String, String> params = new LinkedHashMap<>();// The receiving order is guaranteed here, so linkedhashmap is used
        String s = null, key = null;
        StringBuffer sb = new StringBuffer();
        for (int i = 1, len = strs.length - 1; i < len; i + + ) {
            s = strs[i];
            if (keyRow) {
                key = s.replace("Content-Disposition: form-data; name=", "");
                key = key.substring(1, key.length() - 1);
                keyRow = false;
                bankRow = true;
                sb = new StringBuffer();
                append = false;
                continue;
            }
            if (sep.equals(s)) {
                keyRow = true;
                if (null != key) {
                    params.put(key, sb.toString());
                }
                append = false;
                continue;
            }
            if (bankRow) {
                bankRow = false;
                append = false;
                continue;
            }
            if (append) {
                sb.append("\r\\
");
            }
            sb.append(s);
            append = true;
        }
        if (null != key) {
            params.put(key, sb.toString());
        }
        return JSON.toJSONString(params);//This is alibaba’s fastjson, which needs to introduce dependencies
    }


    @Override
    public int getOrder() {
        return 1;
    }
}