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'
}