• 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长度变化需要重置
  • 相关阅读:
    编码 原码 反码 补码
    java垃圾收集器与内存分配策略
    MYSQL 索引 性能优化
    jvm内存溢出性能调优
    JVM内存模型
    线程相关
    Java 设计模式
    synchronized 与 volatile
    ActivityGroup相关--getLocalActivityManager() 以及intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)用法
    [Android] Activity 重复使用
  • 原文地址:https://www.cnblogs.com/staystand/p/14928087.html
Copyright © 2020-2023  润新知