无论在Spring5的webflux编程或者普通web编程中,只能从request中获取body一次,后面再获取就会报错,但我们有时候会需要获取body中的数据进行加签、验签,这个问题怎么解决呢。
ServerHttpRequestDecorator与ServerWebExchangeDecorator
在Spring-webflux编程中,为我们提供了ServerHttpRequest和ServerWebExchange的包装类,只要我们扩展这两个类,就可以扩展实现我们需要的功能,下面我献上我的示例。
1、封装ServerHttpRequestDecorator类型,构造方法中有个参数是ServerHttpRequest,通过封装getBody()方法,来缓存body内容进行第二次获取
public class PartnerServerHttpRequestDecorator extends ServerHttpRequestDecorator { private final LogUtil logger = new LogUtil(this.getClass()); private final StringWriter cachedCopy = new StringWriter(); private InputStream dataBuffer; private DataBuffer bodyDataBuffer; private int getBufferTime = 0; private byte[] bytes; PartnerServerHttpRequestDecorator(ServerHttpRequest delegate) { super(delegate); } @Override public Flux<DataBuffer> getBody() { // return Flux.just(dataBuffer); if (getBufferTime == 0) { getBufferTime++; Flux<DataBuffer> flux = super.getBody(); return flux .publishOn(single()) .map(this::cache) .doOnComplete(() -> trace(getDelegate(), cachedCopy.toString())); } else { // return Flux.just(dataBuffer); return Flux.just(getBodyMore()); } } private DataBuffer getBodyMore() { NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false)); DataBuffer dataBuffer = nettyDataBufferFactory.wrap(bytes); bodyDataBuffer = dataBuffer; return bodyDataBuffer; } private DataBuffer cache(DataBuffer buffer) { ObjectMapper mapper = new ObjectMapper(); try { dataBuffer = buffer.asInputStream(); // json = mapper.readValue(dataBuffer, JSONObject.class); bytes = IOUtils.toByteArray(dataBuffer); NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false)); DataBuffer dataBuffer = nettyDataBufferFactory.wrap(bytes); bodyDataBuffer = dataBuffer; return bodyDataBuffer; } catch (IOException e) { e.printStackTrace(); } return null; } private void trace(ServerHttpRequest request, String requestBody) { logger.info(requestBody); } }
2、因为在Filter中,传参都是以ServerWebExchange传递参数的,所以我们要再对ServerWebExchange进行一次封装,以便采用我们的requst。
public final class PartnerServerWebExchangeDecorator extends ServerWebExchangeDecorator { private final ServerHttpRequestDecorator requestDecorator; public PartnerServerWebExchangeDecorator(ServerWebExchange delegate) { super(delegate); this.requestDecorator = new PartnerServerHttpRequestDecorator(delegate.getRequest()); } @Override public ServerHttpRequest getRequest() { return requestDecorator; } }
3、然后我们随便找个Filter进行替换即可。
@Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { UsernamePasswordAuthenticationToken auth = (new UsernamePasswordAuthenticationToken("", "", null)); SecurityContextImpl securityContext = new SecurityContextImpl(); securityContext.setAuthentication(auth); //用serverExchange包装类替换SerberWebExchange,以便后续可以进行扩展操作 PartnerServerWebExchangeDecorator exchangeDecorator = new PartnerServerWebExchangeDecorator(exchange); return this.requiresAuthenticationMatcher.matches(exchangeDecorator)
4、后面我们就要对获取的body进行验签了,获取的body格式为FLux<DataBuffer>,我们怎么从中获取到字符串呢,代码如下,可以把DataBuffer读取到一个字节数组里面,注意,读取完后,一定要调用release()方法释放DataBuffer,否则可能造成内存泄漏。
bytes = new byte[buffer.readableByteCount()]; dataBuffer.read(bytes); //释放buffer资源 DataBufferUtils.release(buffer);