一、该问题的重现步骤是什么?
1. 我想在Gateway中做签名验签,但是获取POST传过来的参数一致null,代码如下,试了网上很多写法,都是不行,所以想问问是不是bladex有特殊操作
package com.ctpsp.gateway.filter; import com.ctpsp.gateway.props.AuthProperties; import com.fasterxml.jackson.databind.ObjectMapper; import io.netty.buffer.ByteBufAllocator; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; 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.DataBufferUtils; import org.springframework.core.io.buffer.NettyDataBufferFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; 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.ServerRequest; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.UriComponentsBuilder; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.net.URI; import java.nio.CharBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; /** * Sign认证 * * @author YuHongQian */ @Slf4j @Component @AllArgsConstructor public class SignFilter implements GlobalFilter, Ordered { private AuthProperties authProperties; private ObjectMapper objectMapper; @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { return handleParameter(exchange, chain); } /** * 处理请求(添加全局类参数) * * @param exchange * @param chain * @return */ private Mono<Void> handleParameter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest serverHttpRequest = exchange.getRequest(); HttpMethod method = serverHttpRequest.getMethod(); String contentType = serverHttpRequest.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE); //post请求时,如果是文件上传之类的请求,不修改请求消息体 if (method == HttpMethod.POST && (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equalsIgnoreCase(contentType) || MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(contentType))) { //从请求里获取Post请求体 String bodyStr = resolveBodyFromRequest(serverHttpRequest); //===============================================这行bodyStr为null====================================================================== // 这种处理方式,必须保证post请求时,原始post表单必须有数据过来,不然会报错 if (StringUtils.isBlank(bodyStr)) { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.BAD_REQUEST); return response.setComplete(); } //下面的将请求体再次封装写回到request里,传到下一级,否则,由于请求体已被消费,后续的服务将取不到值 URI uri = serverHttpRequest.getURI(); URI newUri = UriComponentsBuilder.fromUri(uri).build(true).toUri(); ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build(); DataBuffer bodyDataBuffer = stringBuffer(bodyStr); Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer); // 定义新的消息头 HttpHeaders headers = new HttpHeaders(); headers.putAll(exchange.getRequest().getHeaders()); // 由于修改了传递参数,需要重新设置CONTENT_LENGTH,长度是字节长度,不是字符串长度 int length = bodyStr.getBytes().length; headers.remove(HttpHeaders.CONTENT_LENGTH); headers.setContentLength(length); // 设置CONTENT_TYPE if (StringUtils.isNotBlank(contentType)) { headers.set(HttpHeaders.CONTENT_TYPE, contentType); } // 由于post的body只能订阅一次,由于上面代码中已经订阅过一次body。所以要再次封装请求到request才行,不然会报错请求已经订阅过 request = new ServerHttpRequestDecorator(request) { @Override public HttpHeaders getHeaders() { long contentLength = headers.getContentLength(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.putAll(super.getHeaders()); if (contentLength > 0) { httpHeaders.setContentLength(contentLength); } else { // TODO: this causes a 'HTTP/1.1 411 Length Required' on httpbin.org httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked"); } return httpHeaders; } @Override public Flux<DataBuffer> getBody() { return bodyFlux; } }; //封装request,传给下一级 request.mutate().header(HttpHeaders.CONTENT_LENGTH, Integer.toString(bodyStr.length())); return chain.filter(exchange.mutate().request(request).build()); } else if (method == HttpMethod.GET) { // 获取原参数 URI uri = serverHttpRequest.getURI(); StringBuilder query = new StringBuilder(); String originalQuery = uri.getRawQuery(); if (org.springframework.util.StringUtils.hasText(originalQuery)) { query.append(originalQuery); if (originalQuery.charAt(originalQuery.length() - 1) != '&') { query.append('&'); } } // 替换查询参数 URI newUri = UriComponentsBuilder.fromUri(uri) .replaceQuery(query.toString()) .build(true) .toUri(); ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build(); return chain.filter(exchange.mutate().request(request).build()); } return chain.filter(exchange); } /** * 从Flux<DataBuffer>中获取字符串的方法 * * @return 请求体 */ private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) { //获取请求体 Flux<DataBuffer> body = serverHttpRequest.getBody(); AtomicReference<String> bodyRef = new AtomicReference<>(); body.subscribe(buffer -> { CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer()); DataBufferUtils.release(buffer); bodyRef.set(charBuffer.toString()); }); //获取request body return bodyRef.get(); } /** * 字符串转DataBuffer * * @param value * @return */ private 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; } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } }
二、你期待的结果是什么?实际看到的又是什么?
2.4.0
三、你正在使用的是什么产品,什么版本?在什么操作系统上?
四、请提供详细的错误堆栈信息,这很重要。
五、若有更多详细信息,请在下面提供。
官方新品
- 热议问题
-
使用注解配置数据权限
1
-
服务注册上来了但是报403错误
2
-
有没有办法在blade-tsdb-influxdb2里也能打印日志?因为在blade-tsdb-influxdb2中加了些自己的业务代码
1
-
生成的后端代码,swagger调试直接报空指针 DataScopeModel.getResourceCode()
2
-
项目启动nacos报错,还需要配置什么东西吗
1
-
连接sqlserver数据库,主键自增长,批量保存失败
1
-
服务自定义包名,服务注册成功,但swagger中没有api接口
1
-
partial write: points beyond retention policy dropped=1
1
-
这个框架怎么修改数据库连接池数量
1
-
增加新的驱动类
1
扫一扫访问 Blade技术社区 移动端