spring cloud gateway Greenwich modify request body

spring cloud gateway Greenwich modify request body

首先spring cloud gateway的官方文档真的很坑,基本都是基于yml的配置,这种程度的配置在实际的生产环境下是无法使用的,所以我们需要对各个过滤器进行编程的方式重写,主要是参考spring已有的实现类,加以改造。

spring cloud gateway greenwich版本对读取请求体和返回体进行了修改,使用Finchley版本可以生效的代码在greenwich这个版本下始终返回null,已失效!

	HttpHeaders myHeaders = new HttpHeaders();
    copyMultiValueMap(request.getHeaders(), myHeaders);
    myHeaders.remove(HttpHeaders.CONTENT_LENGTH);
    myHeaders.set(HttpHeaders.CONTENT_LENGTH, String.valueOf(len));
    
    Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
    newRequest = new ServerHttpRequestDecorator(newRequest) {
        @Override
        public Flux<DataBuffer> getBody() {	
            return bodyFlux;
        }
        
        @Override
        public HttpHeaders getHeaders() {
        	return myHeaders;           	
        }
    };

    ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();

在实际工作中,网关会携带访问令牌请求授权服务进行授权,授权通过后,会返回UserVo,此时网关需要根据HttpMethod,在queryParams或者request body中添加后端服务需要的用户信息.
.不仅如此,真实的网关中还涉及到url防篡改等逻辑,需要先从请求中读取body进行缓存,再重新构造一个request请求.

		
private Mono<Void> checkAndCache(ServerWebExchange exchange, GatewayFilterChain chain, CacheRequestEntity cacheRequestEntity) {
        return DataBufferUtils.join(exchange.getRequest().getBody())
                .flatMap(dataBuffer -> {
                    DataBufferUtils.retain(dataBuffer);
                    byte[] buf = new byte[dataBuffer.readableByteCount()];
                    dataBuffer.read(buf);
                    String data = new String(buf, StandardCharsets.UTF_8);
                    JSON json = JSON.parseObject(data);
                    String body = json.toJSONString();
                    // 这个cacheRequestEntity是业务中自定义用于缓存request信息的类
                    cacheRequestEntity.setRequestBody(body);
					// 通过上面取出body后,我们可以对body进行body防篡改等业务操作
					
					// 但是从request中取出body后,此时的request是没有body的,继续后续请求会抛出异常.所以我们需要再次重新构造新的request.
                    Mono<String> modifiedBody = Mono.just(body);
                    HttpHeaders headers = new HttpHeaders();
                    headers.putAll(exchange.getRequest().getHeaders());
                    // 删除CONTENT_LENGTH是因为spring cloud源码中重新构造request时候会帮助我们重新计算,而我们如果自己计算会因为json序列化工具不与接收方匹配或者其他一些原因造成contentlength计算不正确.例如gateway使用fastjson进行序列化,而springboot项目默认使用jackson进行反序列化,二者对同样的字符串序列化结果的字数是不一致的
                    headers.remove(HttpHeaders.CONTENT_LENGTH);

					// Greenwich版本通过cachedBodyOutputMessage进行构造body请求
                    CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
                    BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);

                    return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
                        ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
                            @Override
                            public HttpHeaders getHeaders() {
                                long contentLength = headers.getContentLength();
                                HttpHeaders httpHeaders = new HttpHeaders();
                                httpHeaders.putAll(super.getHeaders());
                                // 由于可能对body内容修改,导致contentLength变更,造成json解析失败,所以需要重写设置contentlength
                                if (contentLength > 0) {
                                    httpHeaders.setContentLength(contentLength);
                                } else {
                                    httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                                }
                                return httpHeaders;
                            }

                            @Override
                            public Flux<DataBuffer> getBody() {
                                return outputMessage.getBody();
                            }
                        };

                        return chain.filter(exchange.mutate().request(decorator).build());
                    }));
                });
    }

以上就是spring cloud gateway greenwich版本的请求体 缓存,修改,重新构造.附上生成环境目前使用的版本.

buildscript {
    ext {
        springBootVersion = '2.1.0.RELEASE'
    }
    repositories {
        mavenLocal()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath("io.spring.gradle:dependency-management-plugin:1.0.5.RELEASE")
    }
}
dependencyManagement {
    imports {
        mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Greenwich.RELEASE'
    }
}

dependencies {
    // 3-part tool dependency
    compile('com.google.guava:guava:23.0')
    compile('org.apache.commons:commons-lang3')
    // spring boot and spring cloud dependency
    compile('org.springframework.boot:spring-boot-starter-aop')
    compile('org.springframework.boot:spring-boot-starter-actuator')
    compile('org.springframework.cloud:spring-cloud-starter-gateway')

    compile('org.springframework.cloud:spring-cloud-starter-config:2.1.0.RELEASE')
    compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')
    compile ('org.springframework.cloud:spring-cloud-starter-openfeign')
    compile ('io.github.openfeign:feign-httpclient')
    compile('org.springframework.cloud:spring-cloud-starter-netflix-hystrix')


    // redis dependency
    compile('org.springframework.boot:spring-boot-starter-data-redis-reactive')
    compile('org.apache.commons:commons-pool2')

    // http
    compile('org.apache.httpcomponents:httpclient:4.5.6')
    compile('org.apache.httpcomponents:httpcore:4.4.10')
    // apollo config center
    compile('com.ctrip.framework.apollo:apollo-client:1.0.0')
    
    // test dependency
    testCompile("org.springframework.boot:spring-boot-starter-test")
    testCompile group: 'junit', name: 'junit', version: '4.12'
    testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.4.1'

}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值