1.需要将body复制到exchange,便于之后使用
import io.netty.buffer.UnpooledByteBufAllocator;
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.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
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;
@Slf4j
@Component
public class GlobalCacheRequestBodyFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("GlobalCacheRequestBodyFilter ...");
// // 将 request body 中的内容 copy 一份,记录到 exchange 的一个自定义属性中
// Object cachedRequestBodyObject = exchange.getAttributeOrDefault(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, null);
// // 如果已经缓存过,略过
// if (cachedRequestBodyObject != null) {
// return chain.filter(exchange);
// }
// // 如果没有缓存过,获取字节数组存入 exchange 的自定义属性中
// return DataBufferUtils.join(exchange.getRequest().getBody())
// .map(dataBuffer -> {
// byte[] bytes = new byte[dataBuffer.readableByteCount()];
// dataBuffer.read(bytes);
// DataBufferUtils.release(dataBuffer);
// return bytes;
// }).defaultIfEmpty(new byte[0])
// .doOnNext(bytes -> exchange.getAttributes().put(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, bytes))
// .then(chain.filter(exchange));
//
ServerHttpRequest serverHttpRequest = exchange.getRequest();
String method = serverHttpRequest.getMethodValue();
String contentType = serverHttpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
// 将 request body 中的内容 copy 一份,记录到 exchange 的一个自定义属性中
Object cachedRequestBodyObject = exchange.getAttributeOrDefault(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, null);
// 如果已经缓存过,略过
if (cachedRequestBodyObject != null) {
return chain.filter(exchange);
}
if ("POST".equalsIgnoreCase(method)) {
ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
Mono<String> bodyToMono = serverRequest.bodyToMono(String.class).defaultIfEmpty("");
try {
return bodyToMono.flatMap(body -> {
exchange.getAttributes().put(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, body);
ServerHttpRequest newRequest = new ServerHttpRequestDecorator(serverHttpRequest) {
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
return httpHeaders;
}
@Override
public Flux<DataBuffer> getBody() {
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));
DataBuffer bodyDataBuffer = nettyDataBufferFactory.wrap(body.getBytes());
return Flux.just(bodyDataBuffer);
}
};
return chain.filter(exchange.mutate().request(newRequest).build());
});
}catch (Exception e){
log.info("post body filter err!");
log.info(e.getMessage(),e);
}
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -30;
}
}
2.请求参数解密
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.msl.mafa.common.BaseCode;
import com.msl.mafa.util.AESCBC;
import com.msl.mafa.util.RSAUtil;
import com.msl.mafa.utils.BusinessException;
import lombok.extern.slf4j.Slf4j;
import okhttp3.internal.http2.ErrorCode;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilter;
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.http.HttpHeaders;
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.URI;
import java.util.List;
import java.util.Map;
@Slf4j
@Component
public class AppReqDecryptFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 设置是否加密标识
List<String> paramEncryptHeaders = exchange.getRequest().getHeaders().get(ConstantFilter.PARAM_ENCRYPT);
if(CollectionUtil.isEmpty(paramEncryptHeaders)||!ConstantFilter.REQ_RES_ENCRYPT.equals(paramEncryptHeaders.get(0))){
return chain.filter(exchange);
}
exchange.getAttributes().put(ConstantFilter.PARAM_ENCRYPT, paramEncryptHeaders.get(0));
// 获取请求的方法
ServerHttpRequest oldRequest = exchange.getRequest();
String method = oldRequest.getMethodValue();
URI uri = oldRequest.getURI();
ServerHttpRequest serverHttpRequest = exchange.getRequest();
String contentType = serverHttpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
if ("POST".equals(method)&&"application/json".equals(contentType)){
// 尝试从 exchange 的自定义属性中取出缓存到的 body
Object cachedRequestBodyObject = exchange.getAttributeOrDefault(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, null);
if (cachedRequestBodyObject != null) {
byte[] decrypBytes;
try {
// byte[] body = (byte[]) cachedRequestBodyObject;
String rootData = (String) cachedRequestBodyObject; // 客户端传过来的数据
// decrypBytes = rootData.getBytes();
JSONObject jsonObject = JSONObject.parseObject(rootData);
String encryptKey = (String) jsonObject.get("k");
String encryptData = (String) jsonObject.get("v");
String key = RSAUtil.decode(encryptKey);
String decryptData = AESCBC.decrypt(encryptData, key);
decrypBytes = decryptData.getBytes();
}catch (Exception e){
log.error("客户端数据解析异常:{}", e.toString());
log.error(e.getMessage(),e);
throw new BusinessException(BaseCode.BAD_REQUEST.getCode(), "客户端数据解析异常");
}
// 根据解密后的参数重新构建请求
DataBufferFactory dataBufferFactory = exchange.getResponse().bufferFactory();
Flux<DataBuffer> bodyFlux = Flux.just(dataBufferFactory.wrap(decrypBytes));
ServerHttpRequest newRequest = oldRequest.mutate().uri(uri).build();
newRequest = new ServerHttpRequestDecorator(newRequest) {
@Override
public Flux<DataBuffer> getBody() {
return bodyFlux;
}
};
// 构建新的请求头
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
// 由于修改了传递参数,需要重新设置CONTENT_LENGTH,长度是字节长度,不是字符串长度
int length = decrypBytes.length;
headers.remove(HttpHeaders.CONTENT_LENGTH);
headers.setContentLength(length);
headers.set(ConstantFilter.PARAM_ENCRYPT,ConstantFilter.REQ_RES_ENCRYPT);
// headers.set(HttpHeaders.CONTENT_TYPE, "application/json");
newRequest = new ServerHttpRequestDecorator(newRequest) {
@Override
public HttpHeaders getHeaders() {
return headers;
}
};
// 把解密后的数据重置到exchange自定义属性中,在之后的日志GlobalLogFilter从此处获取请求参数打印日志
exchange.getAttributes().put(ConstantFilter.CACHED_REQUEST_BODY_OBJECT_KEY, decrypBytes);
exchange.getAttributes().put(ConstantFilter.PARAM_ENCRYPT,ConstantFilter.REQ_RES_ENCRYPT);
return chain.filter(exchange.mutate().request(newRequest).build());
}
}else if("GET".equals(method)){ // todo 暂不处理
Map requestQueryParams = oldRequest.getQueryParams();
return chain.filter(exchange);
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -22;
}
}
3.返回参数加密
import com.alibaba.fastjson.JSONObject;
import com.msl.mafa.util.AESCBC;
import com.msl.mafa.util.RSAUtil;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilter;
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.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.Charset;
import java.util.List;
@Component
public class AppRespEncryptFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// String str=exchange.getAttribute(ConstantFilter.PARAM_ENCRYPT);
List<String> paramEncryptHeaders = exchange.getRequest().getHeaders().get(ConstantFilter.PARAM_ENCRYPT);
// String bgDebug = exchange.getAttributeOrDefault(ConstantFilter.PARAM_ENCRYPT, ConstantFilter.REQ_RES_NOT_ENCRYPT);
String bgDebug =paramEncryptHeaders!=null?paramEncryptHeaders.get(0):ConstantFilter.REQ_RES_NOT_ENCRYPT;
ServerHttpResponse originalResponse = exchange.getResponse();
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if(!ConstantFilter.REQ_RES_ENCRYPT.equals(bgDebug)){
return super.writeWith(body);
}
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.buffer().map(dataBuffer -> {
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffer);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
//释放掉内存
DataBufferUtils.release(join);
// 正常返回的数据
String rootData = new String(content, Charset.forName("UTF-8"));
byte[] respData = rootData.getBytes();
if(ConstantFilter.REQ_RES_ENCRYPT.equals(bgDebug)){
// 对数据进行加密
// String randomKey = AESCBC.getRandomKey();
String encryptData = AESCBC.encrypt(rootData);
String encryptRandomKey = RSAUtil.encrypt("Manulife123");
JSONObject json = new JSONObject();
json.put("k", encryptRandomKey);
json.put("v", encryptData);
respData = json.toJSONString().getBytes();
}
// 加密后的数据返回给客户端
byte[] uppedContent = new String(respData, Charset.forName("UTF-8")).getBytes();
originalResponse.getHeaders().setContentLength(uppedContent.length);
return bufferFactory.wrap(uppedContent);
}));
}
return super.writeWith(body);
}
};
return chain.filter(exchange.mutate().response(decoratedResponse).build());
}
@Override
public int getOrder() {
return -25;
}
}
4.注意事项
1)多个filter的加载顺序 |
---|
2)加解密之后CONTENT_LENGTH长度变化需要重置 |