gateway对请求参数加解密

1.需要将body复制到exchange,便于之后使用



import io.netty.buffer.UnpooledByteBufAllocator;
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.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
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;
@Slf4j
@Component
public class GlobalCacheRequestBodyFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("GlobalCacheRequestBodyFilter ...");
//        // 将 request body 中的内容 copy 一份,记录到 exchange 的一个自定义属性中
//        Object cachedRequestBodyObject = exchange.getAttributeOrDefault(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, null);
//        // 如果已经缓存过,略过
//        if (cachedRequestBodyObject != null) {
//            return chain.filter(exchange);
//        }
//        // 如果没有缓存过,获取字节数组存入 exchange 的自定义属性中
//        return DataBufferUtils.join(exchange.getRequest().getBody())
//                .map(dataBuffer -> {
//                    byte[] bytes = new byte[dataBuffer.readableByteCount()];
//                    dataBuffer.read(bytes);
//                    DataBufferUtils.release(dataBuffer);
//                    return bytes;
//                }).defaultIfEmpty(new byte[0])
//                .doOnNext(bytes -> exchange.getAttributes().put(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, bytes))
//                .then(chain.filter(exchange));
//

        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        String method = serverHttpRequest.getMethodValue();
        String contentType = serverHttpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
        // 将 request body 中的内容 copy 一份,记录到 exchange 的一个自定义属性中
        Object cachedRequestBodyObject = exchange.getAttributeOrDefault(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, null);
        // 如果已经缓存过,略过
        if (cachedRequestBodyObject != null) {
            return chain.filter(exchange);
        }
        if ("POST".equalsIgnoreCase(method)) {
            ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
            Mono<String> bodyToMono = serverRequest.bodyToMono(String.class).defaultIfEmpty("");
            try {
                return bodyToMono.flatMap(body -> {
                    exchange.getAttributes().put(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, body);
                    ServerHttpRequest newRequest = new ServerHttpRequestDecorator(serverHttpRequest) {
                        @Override
                        public HttpHeaders getHeaders() {
                            HttpHeaders httpHeaders = new HttpHeaders();
                            httpHeaders.putAll(super.getHeaders());
                            httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                            return httpHeaders;
                        }

                        @Override
                        public Flux<DataBuffer> getBody() {
                            NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));
                            DataBuffer bodyDataBuffer = nettyDataBufferFactory.wrap(body.getBytes());
                            return Flux.just(bodyDataBuffer);
                        }
                    };
                    return chain.filter(exchange.mutate().request(newRequest).build());
                });
            }catch (Exception e){
                log.info("post body filter err!");
                log.info(e.getMessage(),e);
            }
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -30;
    }


}

2.请求参数解密



import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.msl.mafa.common.BaseCode;
import com.msl.mafa.util.AESCBC;
import com.msl.mafa.util.RSAUtil;
import com.msl.mafa.utils.BusinessException;
import lombok.extern.slf4j.Slf4j;
import okhttp3.internal.http2.ErrorCode;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
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.DataBufferFactory;
import org.springframework.http.HttpHeaders;
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.URI;
import java.util.List;
import java.util.Map;
@Slf4j
@Component
public class AppReqDecryptFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 设置是否加密标识
        List<String> paramEncryptHeaders = exchange.getRequest().getHeaders().get(ConstantFilter.PARAM_ENCRYPT);
        if(CollectionUtil.isEmpty(paramEncryptHeaders)||!ConstantFilter.REQ_RES_ENCRYPT.equals(paramEncryptHeaders.get(0))){
            return chain.filter(exchange);
        }
        exchange.getAttributes().put(ConstantFilter.PARAM_ENCRYPT, paramEncryptHeaders.get(0));
        // 获取请求的方法
        ServerHttpRequest oldRequest = exchange.getRequest();
        String method = oldRequest.getMethodValue();
        URI uri = oldRequest.getURI();
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        String contentType = serverHttpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
        if ("POST".equals(method)&&"application/json".equals(contentType)){
            // 尝试从 exchange 的自定义属性中取出缓存到的 body
            Object cachedRequestBodyObject = exchange.getAttributeOrDefault(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, null);
            if (cachedRequestBodyObject != null) {
                byte[] decrypBytes;
                try {
//                    byte[] body = (byte[]) cachedRequestBodyObject;
                    String rootData = (String) cachedRequestBodyObject; // 客户端传过来的数据
//                    decrypBytes = rootData.getBytes();
                    JSONObject jsonObject = JSONObject.parseObject(rootData);
                    String encryptKey = (String) jsonObject.get("k");
                    String encryptData = (String) jsonObject.get("v");
                    String key = RSAUtil.decode(encryptKey);
                    String decryptData = AESCBC.decrypt(encryptData, key);
                    decrypBytes = decryptData.getBytes();

                }catch (Exception e){
                    log.error("客户端数据解析异常:{}", e.toString());
                    log.error(e.getMessage(),e);
                    throw new BusinessException(BaseCode.BAD_REQUEST.getCode(), "客户端数据解析异常");
                }

                // 根据解密后的参数重新构建请求
                DataBufferFactory dataBufferFactory = exchange.getResponse().bufferFactory();
                Flux<DataBuffer> bodyFlux = Flux.just(dataBufferFactory.wrap(decrypBytes));
                ServerHttpRequest newRequest = oldRequest.mutate().uri(uri).build();
                newRequest = new ServerHttpRequestDecorator(newRequest) {
                    @Override
                    public Flux<DataBuffer> getBody() {
                        return bodyFlux;
                    }
                };

                // 构建新的请求头
                HttpHeaders headers = new HttpHeaders();
                headers.putAll(exchange.getRequest().getHeaders());
                // 由于修改了传递参数,需要重新设置CONTENT_LENGTH,长度是字节长度,不是字符串长度
                int length = decrypBytes.length;
                headers.remove(HttpHeaders.CONTENT_LENGTH);
                headers.setContentLength(length);
                headers.set(ConstantFilter.PARAM_ENCRYPT,ConstantFilter.REQ_RES_ENCRYPT);
                // headers.set(HttpHeaders.CONTENT_TYPE, "application/json");
                newRequest = new ServerHttpRequestDecorator(newRequest) {
                    @Override
                    public HttpHeaders getHeaders() {
                        return headers;
                    }
                };

                // 把解密后的数据重置到exchange自定义属性中,在之后的日志GlobalLogFilter从此处获取请求参数打印日志
                exchange.getAttributes().put(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, decrypBytes);
                exchange.getAttributes().put(ConstantFilter.PARAM_ENCRYPT,ConstantFilter.REQ_RES_ENCRYPT);
                return chain.filter(exchange.mutate().request(newRequest).build());
            }
        }else if("GET".equals(method)){ // todo 暂不处理
            Map requestQueryParams = oldRequest.getQueryParams();
            return chain.filter(exchange);
        }
        return chain.filter(exchange);
    }


    @Override
    public int getOrder() {
        return -22;
    }



}


3.返回参数加密


import com.alibaba.fastjson.JSONObject;
import com.msl.mafa.util.AESCBC;
import com.msl.mafa.util.RSAUtil;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilter;
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.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.Charset;
import java.util.List;

@Component
public class AppRespEncryptFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//        String str=exchange.getAttribute(ConstantFilter.PARAM_ENCRYPT);
        List<String> paramEncryptHeaders = exchange.getRequest().getHeaders().get(ConstantFilter.PARAM_ENCRYPT);
//        String bgDebug = exchange.getAttributeOrDefault(ConstantFilter.PARAM_ENCRYPT, ConstantFilter.REQ_RES_NOT_ENCRYPT);
        String bgDebug =paramEncryptHeaders!=null?paramEncryptHeaders.get(0):ConstantFilter.REQ_RES_NOT_ENCRYPT;
        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = originalResponse.bufferFactory();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if(!ConstantFilter.REQ_RES_ENCRYPT.equals(bgDebug)){
                    return super.writeWith(body);
                }
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    return super.writeWith(fluxBody.buffer().map(dataBuffer -> {
                        DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
                        DataBuffer join = dataBufferFactory.join(dataBuffer);

                        byte[] content = new byte[join.readableByteCount()];
                        join.read(content);
                        //释放掉内存
                        DataBufferUtils.release(join);
                        // 正常返回的数据
                        String rootData = new String(content, Charset.forName("UTF-8"));
                        byte[] respData = rootData.getBytes();

                        if(ConstantFilter.REQ_RES_ENCRYPT.equals(bgDebug)){
                            // 对数据进行加密
//                            String randomKey = AESCBC.getRandomKey();
                            String encryptData = AESCBC.encrypt(rootData);
                            String encryptRandomKey = RSAUtil.encrypt("Manulife123");
                            JSONObject json = new JSONObject();
                            json.put("k", encryptRandomKey);
                            json.put("v", encryptData);
                            respData = json.toJSONString().getBytes();
                        }

                        // 加密后的数据返回给客户端
                        byte[] uppedContent = new String(respData, Charset.forName("UTF-8")).getBytes();
                        originalResponse.getHeaders().setContentLength(uppedContent.length);
                        return bufferFactory.wrap(uppedContent);
                    }));
                }
                return super.writeWith(body);
            }
        };
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }

    @Override
    public int getOrder() {
        return -25;
    }

}


4.注意事项

1)多个filter的加载顺序
2)加解密之后CONTENT_LENGTH长度变化需要重置

gateway对请求参数加解密

上一篇:用docker搭建jenkins


下一篇:celery相关问题