用springcloud-gateway写了一个过滤器,用于解密前端请求的加密参数,但是如果请求post或put请求后自动加载列表接口就会报错400 Bad Request,后端报错实在列表接口对应的模块;请求get或delete请求后自动加载列表接口正常;但是如果是F5刷新页面之后第一次请求put或post之后的自动加载能成功!被这个问题问题困扰几天了还没解决!!!
以下是过滤器代码
package com.example.gateway.filter;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ObjectUtil;
import com.example.gateway.filter.decorator.CachedBodyServerHttpRequestDecorator;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
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.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import com.example.gateway.filter.decorator.ResponseEncryptDecorator;
import com.example.gateway.filter.utils.CryptoUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
@Slf4j
@Component
public class CustomCryptoFilter implements GlobalFilter, Ordered {
/**
* 自定义加密过滤器的处理方法
* 此方法用于处理进入的请求,检查是否需要解密请求体,并在需要时加密响应
* 它首先检查请求头以确定是否需要解密请求体如果不需要解密,则直接继续执行链中的下一个过滤器
* 如果需要解密,它会解密请求体,设置响应头以指示响应将被加密,然后继续执行链中的下一个过滤器
* 此外,它还尝试解密请求参数中的'params',并在成功解密后重建URI
*
* @param exchange 服务器网页交换对象,包含请求和响应
* @param chain 网关过滤器链,用于执行下一个过滤器
* @return 返回一个Mono<Void>,表示异步处理完成
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
// 检查是否启用加密通信
HttpHeaders headers = request.getHeaders();
boolean shouldDecrypt = ObjectUtil.isNotEmpty(headers.get("X-Request-Encrypt")) &&
"AES".equals(headers.getFirst("X-Request-Encrypt"));
if (!shouldDecrypt) {
return chain.filter(exchange);
}
// 使用 ResponseEncryptDecorator 装饰响应对象
// ServerHttpResponse decoratedResponse = new ResponseEncryptDecorator(response);
String method = request.getMethodValue();
// 是否是需要解密 body 的方法(POST/PUT)
boolean isBodyMethod = "POST".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method);
// 异步处理 body 解密(仅限 POST/PUT)
Mono<ServerHttpRequest> decoratedRequestMono;
if (isBodyMethod) {
decoratedRequestMono = DataBufferUtils.join(request.getBody())
.flatMap(dataBuffer -> {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
DataBufferUtils.release(dataBuffer); // ✅ 释放 buffer 资源
String body = new String(bytes, StandardCharsets.UTF_8);
if (ObjectUtil.isNotEmpty(body)) {
try {
body = URLDecoder.decode(body, StandardCharsets.UTF_8.name());
String decryptedBody = CryptoUtils.decrypt(body);
decryptedBody = URLDecoder.decode(decryptedBody, StandardCharsets.UTF_8.name());
return Mono.just(new CachedBodyServerHttpRequestDecorator(
exchange.getRequest(), decryptedBody.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
log.error("Failed to decrypt request body", e);
return Mono.just(exchange.getRequest()); // ⚠️ 解密失败仍继续执行
}
} else {
return Mono.just(exchange.getRequest());
}
})
.onErrorResume(ex -> {
log.warn("Error reading or decrypting body, using original request", ex);
return Mono.just(exchange.getRequest());
});
} else {
// 非 body 方法直接返回原始请求
decoratedRequestMono = Mono.just(exchange.getRequest());
// 对于 GET/DELETE 请求,强制清空 body
// decoratedRequestMono = Mono.just(new EmptyBodyRequestDecorator(exchange.getRequest()));
}
// 不管什么方法,只要存在 params 就尝试解密
Mono<URI> newUriMono = Mono.fromSupplier(() -> {
MultiValueMap<String, String> valueMap = request.getQueryParams();
if (ObjectUtil.isEmpty(valueMap)) {
return UriComponentsBuilder.fromUri(request.getURI()).build().toUri();
}
String encrypted = valueMap.getFirst("params");
if (ObjectUtil.isEmpty(encrypted)) {
return UriComponentsBuilder.fromUri(request.getURI()).build().toUri();
}
try {
encrypted = URLDecoder.decode(encrypted, StandardCharsets.UTF_8.name());
encrypted = Base64.decodeStr(encrypted);
String decrypted = CryptoUtils.decrypt(encrypted);
decrypted = URLDecoder.decode(decrypted, StandardCharsets.UTF_8.name());
return UriComponentsBuilder.fromUri(request.getURI())
.replaceQuery(rebuildUri(decrypted).build().getQuery())
.build()
.toUri();
} catch (Exception e) {
log.warn("Failed to decrypt query params", e);
return UriComponentsBuilder.fromUri(request.getURI()).build().toUri();
}
});
return Mono.zip(
decoratedRequestMono.defaultIfEmpty(exchange.getRequest()),
newUriMono.defaultIfEmpty(UriComponentsBuilder.fromUri(request.getURI()).build().toUri())
)
.map(tuple -> {
ServerHttpRequest decoratedRequest = tuple.getT1();
URI newUri = tuple.getT2();
ServerWebExchange ec =
exchange.mutate()
.request(newUri != null ? decoratedRequest.mutate().uri(newUri).build() : decoratedRequest)
// .response(decoratedResponse)
.build();
ServerHttpRequest re = ec.getRequest();
HttpHeaders hds = re.getHeaders();
log.info("---------------请求头-------------: {}", hds);
log.info("---------------请求uri-------------: {}", re.getURI());
return ec;
})
.flatMap(mutatedExchange -> chain.filter(mutatedExchange));
}
/**
* 构建新的 URI 查询参数
*/
private UriComponentsBuilder rebuildUri(String decrypted) {
UriComponentsBuilder queryBuilder = UriComponentsBuilder.newInstance();
for (String param : decrypted.split("&")) {
if (param.contains("=")) {
String[] kv = param.split("=", 2);
String key = kv[0];
String value = kv.length > 1 ? kv[1] : "";
if (!value.isEmpty()) {
queryBuilder.queryParam(key, value);
}
}
}
return queryBuilder;
}
@Override
public int getOrder() {
return -10; // 确保在大部分 Filter 之前执行
}
}