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?
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With