Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring WebClient can't read request body before send with ExchangeFilterFunction

All the time I used RestTemplate and decided to switch to WebClient.

Before sending a request, I sign request body with a private key and the client checks the request with a public one.

My interceptor:

private static class SignatureClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

    private final PrivateKey privateKey;

    private SignatureClientHttpRequestInterceptor(String privateKeyLocation) {
        this.privateKey = PemUtils.getPrivateKey(Paths.get(privateKeyLocation));
    }

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        if (request.getMethod() == HttpMethod.POST) {
            request.getHeaders().add("X-Signature", Base64.getEncoder().encodeToString(PemUtils.signData(privateKey, SignatureAlgorithm.RS256.getJcaName(), body)));
        }
        return execution.execute(request, body);
    }
}

But at WebClient I did not find such an opportunity in ExchangeFilterFunction.

Is there anyway to do this in WebClient or do I have to manually sign the request body before sending it?

like image 344
Peter Kozlovsky Avatar asked Jul 04 '26 01:07

Peter Kozlovsky


1 Answers

Signing the body would require it in serialized form, but serialization happens just before sending the data so it needs to be intercepted somehow.

In the case of JSON content, you can create your own Encoder (wrapping the existing Jackson2JsonEncoder for example) and passing this as an ExchangeStrategies when building the WebClient. After the serialized data is intercepted, you can inject the headers. But the Encoder does not have a reference to the ClientHttpRequest so you will need to capture this object in an HttpConnector and pass it in the SubscriberContext.

This blog post explains the process: https://andrew-flower.com/blog/Custom-HMAC-Auth-with-Spring-WebClient#s-post-data-signing

As an example, your WebClient creation step might look like below, where MessageCapturingHttpConnector is a connector that captures the ClientHttpRequest and BodyCapturingJsonEncoder

Signer signer = new Signer(clientId, secret);
MessageSigningHttpConnector httpConnector = new MessageSigningHttpConnector();
BodyCapturingJsonEncoder bodyCapturingJsonEncoder
    = new BodyCapturingJsonEncoder(signer);

WebClient client
    = WebClient.builder()
               .exchangeFunction(ExchangeFunctions.create(
                       httpConnector,
                       ExchangeStrategies
                              .builder()
                               .codecs(clientDefaultCodecsConfigurer -> {
                                   clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(bodyCapturingJsonEncoder);
                                   clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(new ObjectMapper(), MediaType.APPLICATION_JSON));
                               })
                               .build()
               ))
               .baseUrl(String.format("%s://%s/%s", environment.getProtocol(), environment.getHost(), environment.getPath()))
               .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
               .build();
like image 167
rewolf Avatar answered Jul 06 '26 14:07

rewolf



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!