在我通过 SpringCloud-Gateway 修改 requestBody 和 responseBody 后,我发送了请求,它第一次返回了“200”的状态码。
但是当我再次发送这个请求时,它返回了“400 bad request”!
我尝试了很多次,但这种情况仍然存在。相关类如下。
RequestEncryptionGlobalFilter.java
package com.jovian.gateway.filter;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.jovian.gateway.api.CommonResult;
import com.jovian.gateway.constant.CacheConstant;
import com.jovian.gateway.constant.CommonConstant;
import com.jovian.gateway.service.ServerLogService;
import com.jovian.gateway.uitl.JwtUtil;
import com.jovian.gateway.uitl.RedisUtil;
import com.jovian.gateway.uitl.oConvertUtils;
import com.jovian.gateway.vo.LoginUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.cloud.gateway.support.DefaultServerRequest;
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.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
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;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author h
* @date 2021/10/12
*/
@Slf4j
@Component
public class RequestEncryptionGlobalFilter implements GlobalFilter, Ordered {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private ServerLogService serverLogService;
private final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();
@Autowired
private RedisUtil redisUtil;
@Override
public int getOrder() {
return -2;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// return chain.filter(exchange);
return processRequest(exchange, chain);
}
private Mono<Void> processRequest(ServerWebExchange exchange, GatewayFilterChain chain) {
//CA
ServerHttpRequest request = exchange.getRequest();
ServerLog serverLog = new ServerLog();
serverLog.setCreateBy("admin");
serverLog.setCreateTime(LocalDateTime.now());
serverLog.setRequestTime(DateUtil.now());
serverLog.setRequestIp(oConvertUtils.getIpAddrByRequest(request));
String token = exchange.getRequest().getHeaders().getFirst("X-Access-Token");
if(!checkUserTokenIsEffect(token,serverLog)){
ServerHttpResponse baseExchange = exchange.getResponse();
baseExchange.setStatusCode(HttpStatus.OK);
baseExchange.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
String body= JSONUtil.toJsonStr(CommonResult.unauthorized("errorMessage1"));
DataBuffer buffer = baseExchange.bufferFactory().wrap(body.getBytes(Charset.forName("UTF-8")));
return baseExchange.writeWith(Mono.just(buffer));
}
RequestPath path = request.getPath();
serverLog.setServerResourceId(path.toString());
serverLogService.saveOrUpdate(serverLog);
exchange.getRequest().mutate().header("logId", serverLog.getId()).build();
ServerRequest serverRequest = new DefaultServerRequest(exchange, messageReaders);
DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
Mono<String> rawBody = serverRequest.bodyToMono(String.class).map(s -> s);
BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(rawBody, String.class);
HttpHeaders tempHeaders = new HttpHeaders();
tempHeaders.putAll(exchange.getRequest().getHeaders());
tempHeaders.remove(HttpHeaders.CONTENT_LENGTH);
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, tempHeaders);
return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
Flux<DataBuffer> body = outputMessage.getBody();
DataBufferHolder holder = new DataBufferHolder();
body.subscribe(dataBuffer -> {
int len = dataBuffer.readableByteCount();
holder.length = len;
byte[] bytes = new byte[len];
dataBuffer.read(bytes);
DataBufferUtils.release(dataBuffer);
String text = new String(bytes, StandardCharsets.UTF_8);
log.info("text:{}",text);
JsonNode jsonNode = readNode(text);
DataBuffer data = bufferFactory.allocateBuffer();
serverLog.setRequestParam(text);
serverLogService.saveOrUpdate(serverLog);
data.write(jsonNode.toString().getBytes(StandardCharsets.UTF_8));
holder.dataBuffer = data;
});
ServerHttpRequestDecorator requestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public HttpHeaders getHeaders() {
long contentLength = tempHeaders.getContentLength();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
if (contentLength > 0) {
httpHeaders.setContentLength(contentLength);
} else {
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
}
return httpHeaders;
}
@Override
public Flux<DataBuffer> getBody() {
return Flux.just(holder.dataBuffer);
}
};
return chain.filter(exchange.mutate().request(requestDecorator).build());
}));
}
private void rewritePayloadNode(String text, JsonNode root) {
try {
JsonNode node = objectMapper.readTree(text);
ObjectNode objectNode = (ObjectNode) root;
objectNode.set("payload", node);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
private void setPayloadTextNode(String text, JsonNode root) {
try {
ObjectNode objectNode = (ObjectNode) root;
objectNode.set("payload", new TextNode(text));
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
private JsonNode readNode(String in) {
try {
return objectMapper.readTree(in);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
private class DataBufferHolder {
DataBuffer dataBuffer;
int length;
}
public Boolean checkUserTokenIsEffect(String token,ServerLog serverLog) {
if (StrUtil.isBlank(token)){
return false;
}
String username = JwtUtil.getUsername(token);
if (username == null) {
return false;
}
ArrayList loginUsers = (ArrayList) redisUtil.get(CacheConstant.SYS_USERS_CACHE_JWT + ":" + token);
if(loginUsers==null||loginUsers.size()==0){
return false;
}
LoginUser loginUser = BeanUtil.mapToBean((Map<?, ?>) loginUsers.get(1),LoginUser.class,true) ;
if (loginUser.getStatus() != 1) {
return false;
}
if (!jwtTokenRefresh(token, username, loginUser.getPassword())) {
return false;
}
serverLog.setUserId(loginUser.getId());
return true;
}
public boolean jwtTokenRefresh(String token, String userName, String passWord) {
String cacheToken = String.valueOf(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN + token));
if (StrUtil.isNotBlank(cacheToken)) {
if (!JwtUtil.verify(cacheToken, userName, passWord)) {
String newAuthorization = JwtUtil.sign(userName, passWord);
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, newAuthorization);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME *2 / 1000);
}
return true;
}
return false;
}
}
ResponseDecryptionGlobalFilter.java
package com.jovian.gateway.filter;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;
import com.jovian.gateway.api.CommonResult;
import com.jovian.gateway.service.ServerLogService;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.cloud.gateway.support.DefaultClientResponse;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ReactiveHttpOutputMessage;
import org.springframework.http.ResponseCookie;
import org.springframework.http.client.reactive.ClientHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR;
/**
* @author h
* @date 2021/10/12
*/
@Slf4j
@Component
public class ResponseDecryptionGlobalFilter implements GlobalFilter, Ordered {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private ServerLogService serverLogService;
@Override
public int getOrder() {
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return processResponse(exchange, chain);
}
private Mono<Void> processResponse(ServerWebExchange exchange, GatewayFilterChain chain) {
String logId = exchange.getRequest().getHeaders().getFirst("logId");
ServerLog serverLog = new ServerLog();
serverLog.setId(logId);
log.info(logId);
ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(exchange.getResponse()) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
String originalResponseContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_TYPE, originalResponseContentType);
ResponseAdapter responseAdapter = new ResponseAdapter(body, httpHeaders);
DefaultClientResponse clientResponse = new DefaultClientResponse(responseAdapter, ExchangeStrategies.withDefaults());
Mono<String> rawBody = clientResponse.bodyToMono(String.class).map(s -> s);
BodyInserter<Mono<String>, ReactiveHttpOutputMessage> bodyInserter = BodyInserters.fromPublisher(rawBody, String.class);
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, exchange.getResponse().getHeaders());
return bodyInserter.insert(outputMessage, new BodyInserterContext())
.then(Mono.defer(() -> {
Flux<DataBuffer> messageBody = outputMessage.getBody();
Flux<DataBuffer> flux = messageBody.map(buffer -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
DataBufferUtils.release(buffer);
JsonNode jsonNode = readNode(charBuffer.toString());
String text = jsonNode.toString();
serverLog.setResponseData(text);
serverLog.setResponseStatus(getStatusCode().toString());
serverLogService.saveOrUpdate(serverLog);
JSONObject jsonObject = JSONUtil.parseObj(text);
CommonResult<JSONObject> success = CommonResult.success(jsonObject);
String s = JSONUtil.toJsonStr(success);
return getDelegate().bufferFactory().wrap(s.getBytes(StandardCharsets.UTF_8));
});
HttpHeaders headers = getDelegate().getHeaders();
if (!headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) {
flux = flux.doOnNext(data -> headers.setContentLength(data.readableByteCount()));
}
return getDelegate().writeWith(flux);
}));
}
};
return chain.filter(exchange.mutate().response(responseDecorator).build());
}
private void setPayloadTextNode(String text, JsonNode root) {
try {
ObjectNode objectNode = (ObjectNode) root;
objectNode.set("payload", new TextNode(text));
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
private JsonNode readNode(String in) {
try {
return objectMapper.readTree(in);
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
private class ResponseAdapter implements ClientHttpResponse {
private final Flux<DataBuffer> flux;
private final HttpHeaders headers;
@SuppressWarnings("unchecked")
private ResponseAdapter(Publisher<? extends DataBuffer> body, HttpHeaders headers) {
this.headers = headers;
if (body instanceof Flux) {
flux = (Flux) body;
} else {
flux = ((Mono) body).flux();
}
}
@Override
public Flux<DataBuffer> getBody() {
return flux;
}
@Override
public HttpHeaders getHeaders() {
return headers;
}
@Override
public HttpStatus getStatusCode() {
return null;
}
@Override
public int getRawStatusCode() {
return 0;
}
@Override
public MultiValueMap<String, ResponseCookie> getCookies() {
return null;
}
}
}

