首页 > 解决方案 > WebFlux WebClient 不发送证书

问题描述

我正在创建一个基于 Spring WebFlux 的 Java 客户端,它应该向外部端点发送请求,并通过证书进行身份验证。我使用 Spring 的 WebServiceTemplate 做了类似的事情,通过在 application.yaml 文件中指定证书,如下所示:

client:
  ssl:
    enabled: true
    key-store: /myapp/certificate/keystore.p12
    key-store-password: SUPERSECRET

然后通过为 HttpClient 定义 SSLContext 以生成用于正确调用的相关 bean。这是上下文配置的摘录:

private SSLContext sslContext()
  throws IOException, CertificateException, NoSuchAlgorithmException, KeyStoreException,
  KeyManagementException, UnrecoverableKeyException {

  return SSLContextBuilder.create()
    .loadKeyMaterial(new File(keystore), keystorePassword.toCharArray(),
        keystorePassword.toCharArray()).build();
}

事实证明,我无法通过使用 WebClient(来自 WebFlux)来执行完全相同的操作。根据各种 SO 响应和指南,我所做的如下:

通过加载相同的内容来创建正确的 SSLContext keystore.p12(我有意从以下代码中删除了 try-catch 以提高可读性):

private WebClient getMtlsWebClient() {
  HttpClient httpClient = HttpClient.create();

  httpClient.secure(spec -> {
    KeyStore keyStore = KeyStore.getInstance("PKCS12");
    keyStore.load(new FileInputStream(ResourceUtils.getFile(keyStorePath)), keyStorePass.toCharArray());

    // Set up key manager factory to use key-store
    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    keyManagerFactory.init(keyStore, keyStorePass.toCharArray());
    
    spec.sslContext(SslContextBuilder.forClient()
        .keyManager(keyManagerFactory)
        .build());
  });

  return WebClient
      .builder()
      .clientConnector(new ReactorClientHttpConnector(httpClient))
      .build();
}

然后像下面这样简单地触发调用:

return getMtlsWebClient()
    .post()
    .uri(externalServiceUrl)
    .contentType(MediaType.TEXT_XML)
    .accept(MediaType.TEXT_XML)
    .acceptCharset(StandardCharsets.UTF_8)
    .body(Mono.just(request), reqClazz)
    .retrieve()
    .bodyToMono(resClazz)
    .retryWhen(
        Retry
            .fixedDelay(3, Duration.ofSeconds(1))
            .filter(this::isAnyError)
    ).
    map(res -> {
      log.trace("Response payload: {}", res);
      return res;
    })
    .onErrorMap(res -> {
      log.error("Error response payload: {}", res);
      return res;
    });

不幸的是,我得到的响应是 403 FORBIDDEN,而且握手日志确实显示以下消息:

*** ServerHelloDone
[read] MD5 and SHA1 hashes:  len = 4
0000: 0E 00 00 00                                        ....
Warning: no suitable certificate found - continuing without client authentication
*** Certificate chain
<Empty>
***

虽然以前的 WebServiceTemplate 可以正常工作(我省略了证书链):

*** ServerHelloDone
[read] MD5 and SHA1 hashes:  len = 4
0000: 0E 00 00 00                                        ....
matching alias: myalias
*** Certificate chain

谁能建议我在上下文配置中缺少什么?

预先感谢您的帮助!

如果您需要更多信息,请告诉我...

标签: javaspring-bootsslspring-webfluxclient-certificates

解决方案


推荐阅读