1. CacheRequestFilter 全局过滤器 解决body流重复获取
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.DefaultDataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpMethod;
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;
@Component
public class CacheRequestFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
if (request.getHeaders().getContentType() == null) {
return chain.filter(exchange);
}
HttpMethod method = request.getMethod();
if (method == null || method.matches("GET") || method.matches("DELETE")) {
return chain.filter(exchange);
}
//当body中没有缓存时,只会执行这一个拦截器, 原因是fileMap中的代码没有执行,所以需要在为空时构建一个空的缓存
DefaultDataBufferFactory defaultDataBufferFactory = new DefaultDataBufferFactory();
DefaultDataBuffer defaultDataBuffer = defaultDataBufferFactory.allocateBuffer(0);
//构建新数据流, 当body为空时,构建空流
Flux<DataBuffer> bodyDataBuffer = exchange.getRequest().getBody().defaultIfEmpty(defaultDataBuffer);
return DataBufferUtils.join(bodyDataBuffer)
.flatMap(dataBuffer -> {
DataBufferUtils.retain(dataBuffer);
Flux<DataBuffer> cachedFlux = Flux
.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
return cachedFlux;
}
};
//exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, cachedFlux);
return chain.filter(exchange.mutate().request(mutatedRequest).build());
});
}
@Override
public int getOrder() {
return 0;
}
}
2.加解密的类型包括json/ form-data/x-www-form-urlencoded
3. 对body 里的参数 进行加解密 和 增删参数 进行向下传递 (这里采用AES加解密)
package com.suntae.gateway.filter;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.suntae.common.domain.ResultException;
import com.suntae.gateway.utils.AESUtils;
import io.netty.buffer.ByteBufAllocator;
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.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
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.URLDecoder;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
@Slf4j
@Component
public class DeCodeGlobalFilter implements GlobalFilter, Ordered {
private final static String sk = "aes密钥";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
HttpMethod method = request.getMethod();
long contentLength = request.getHeaders().getContentLength();
if (contentLength > 0) {
//只针对post请求解码
if (HttpMethod.POST.equals(method)) {
return handlePostMethod(exchange, request, chain);
}
}
return chain.filter(exchange);
}
private Mono<Void> handlePostMethod(ServerWebExchange exchange, ServerHttpRequest request, GatewayFilterChain chain) {
MediaType contentType = request.getHeaders().getContentType();
if (contentType == null) {
return Mono.error(new ResultException("未识别的请求体类型"));
}
StringBuffer paramBuffer = new StringBuffer();
Flux<DataBuffer> body = exchange.getRequest().getBody();
body.subscribe(buffer -> {
//解析body 数据
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
//在这里解密 并 重新塞进去
paramBuffer.append(charBuffer);
});
//获取到最后的body 中的数据
String param = paramBuffer.toString();
JSONObject JsonData = null;
if (MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) {
JsonData = JSONObject.parseObject(param);
}
if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) {
JsonData = handleFormURLEncoded(param);
}
if (MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) {
JsonData = JSON.parseObject(handleFormData(param, contentType.toString()));
}
if (JsonData == null) {
return Mono.error(new ResultException("不支持的请求体类型"));
}
for (String str : JsonData.keySet()) {
log.info("参数:{}", str);
log.info("{}解密前:{}", str, JsonData.get(str));
//解密前的值
String enCode = JsonData.getString(str);
String deCode;
try {
deCode = AESUtils.decrypt(URLDecoder.decode(enCode, "UTF-8"), sk);
} catch (Exception e) {
throw new ResultException("解密失败");
}
// 解密后的
log.info("{}解密后:{}", str, deCode);
param = param.replace(enCode, deCode);
}
String bodyStr = param;
DataBuffer bodyDataBuffer = stringBuffer(bodyStr);
Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
exchange.getRequest()) {
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
int length = bodyStr.getBytes().length;
httpHeaders.putAll(super.getHeaders());
httpHeaders.remove(HttpHeaders.CONTENT_TYPE);
httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);
httpHeaders.setContentLength(length);
httpHeaders.set(HttpHeaders.CONTENT_TYPE, contentType.toString());
// 设置CONTENT_TYPE
return httpHeaders;
}
@Override
public Flux<DataBuffer> getBody() {
return bodyFlux;
}
};
return chain.filter(exchange.mutate().request(mutatedRequest).build());
}
private JSONObject handleFormURLEncoded(String param) {
JSONObject jsonObject = new JSONObject();
String[] split = param.split("&");
for (String s : split) {
String[] split1 = s.split("=");
jsonObject.put(split1[0], split1[1]);
}
return jsonObject;
}
protected DataBuffer stringBuffer(String value) {
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
}
private String handleFormData(String str, String contentType) {
String sep = "--" + contentType.replace("multipart/form-data;boundary=", "");
String[] strs = str.split("\r\n");
boolean bankRow = false;// 空行
boolean keyRow = true;// name=xxx行
boolean append = false;// 内容是否拼接换行符
Map<String, String> params = new LinkedHashMap<>();// 这里保证接收顺序,所以用linkedhashmap
String s = null, key = null;
StringBuffer sb = new StringBuffer();
for (int i = 1, len = strs.length - 1; i < len; i++) {
s = strs[i];
if (keyRow) {
key = s.replace("Content-Disposition: form-data; name=", "");
key = key.substring(1, key.length() - 1);
keyRow = false;
bankRow = true;
sb = new StringBuffer();
append = false;
continue;
}
if (sep.equals(s)) {
keyRow = true;
if (null != key) {
params.put(key, sb.toString());
}
append = false;
continue;
}
if (bankRow) {
bankRow = false;
append = false;
continue;
}
if (append) {
sb.append("\r\n");
}
sb.append(s);
append = true;
}
if (null != key) {
params.put(key, sb.toString());
}
return JSON.toJSONString(params);// 这里是alibaba的fastjson,需要引入依赖
}
@Override
public int getOrder() {
return 1;
}
}