Spring WebClient在使用ExchangeFilterFunction

时间:2019-04-10 15:16:54

标签: java spring spring-webflux

我一直使用RestTemplate并决定切换到WebClient。

在发送请求之前,我使用私钥在请求正文上签名,客户端与公共请求一起检查请求。

我的拦截器:

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);
    }
}

但是在WebClient上,我没有在ExchangeFilterFunction中找到这样的机会。

无论如何,WebClient都可以执行此操作吗?还是必须在发送请求正文之前手动对其进行签名?

1 个答案:

答案 0 :(得分:0)

鉴于WebClient的反应性,这不是一件简单的事情。签署主体需要序列化形式,但是序列化发生在发送数据之前,因此需要以某种方式进行拦截。老实说,我认为在以后的更新中需要考虑这一点,但是我确实找到了一种稍微绕行的方法。

对于JSON内容,您可以创建自己的编码器(例如包装现有的Jackson2JsonEncoder),并在构建ExchangeStrategies时将其作为WebClient传递。截获序列化数据后,可以插入标头。但是Encoder并没有引用ClientHttpRequest,因此您需要及早捕获该对象以在序列化数据时稍后进行修改。这可以通过包装默认HttpConnector并将其传入作为自定义ExchangeFunction

例如,您的WebClient创建步骤可能如下所示,其中MessageCapturingHttpConnector是捕获ClientHttpRequestBodyCapturingJsonEncoder

的连接器
Signer signer = new Signer(clientId, secret);
MessageSigningHttpConnector httpConnector = new MessageSigningHttpConnector(signer);
BodyCapturingJsonEncoder bodyCapturingJsonEncoder
    = new BodyCapturingJsonEncoder(httpConnector::signWithBody);

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();

看看这篇博客文章,它展示了一种实现方法:

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

希望它能帮助您获得想要的东西。