使用ssl的Spring 5 WebClient

Seb*_*Seb 14 ssl spring self-signed reactive-programming spring-webflux

我正在尝试查找WebClient使用的示例.我的目标是使用Spring 5 WebClient使用https和自签名证书查询REST服务

任何例子?

Ven*_*lin 14

请参阅使用不安全的TrustManagerFactory的示例,它信任所有X.509证书(包括自签名)而无需任何验证.文档中的重要说明:

切勿在生产中使用此TrustManagerFactory.它纯粹是出于测试目的,因此非常不安全.

@Bean
public WebClient createWebClient() throws SSLException {
    SslContext sslContext = SslContextBuilder
            .forClient()
            .trustManager(InsecureTrustManagerFactory.INSTANCE)
            .build();
    ClientHttpConnector httpConnector = HttpClient.create().secure { t -> t.sslContext(sslContext) }
    return WebClient.builder().clientConnector(httpConnector).build();
}
Run Code Online (Sandbox Code Playgroud)

  • 此解决方案在将 Spring Boot 升级到 2.1.0 并引入 Spring 5.1.1 后停止工作,/sf/answers/3720334201/ 这对我使用 Spring Security 5.1.1 有效。 (7认同)

Mun*_*del 11

看起来像是从Spring 5.1.1(Spring boot 2.1.0)中删除HttpClientOptionsReactorClientHttpConnector,因此在创建实例时无法配置选项ReactorClientHttpConnector

现在可以使用的一个选项是:

val sslContext = SslContextBuilder
            .forClient()
            .trustManager(InsecureTrustManagerFactory.INSTANCE)
            .build()
val httpClient = HttpClient.create().secure { t -> t.sslContext(sslContext) }
val webClient = WebClient.builder().clientConnector(ReactorClientHttpConnector(httpClient)).build()
Run Code Online (Sandbox Code Playgroud)

基本上在创建HttpClient时,我们正在配置不安全的 sslContext,然后传递此httpClient以在ReactorClientHttpConnector全局中使用.

另一个选项TcpClient使用不安全的sslContext 进行配置并使用它来创建HttpClient实例,如下所示:

val sslContext = SslContextBuilder
            .forClient()
            .trustManager(InsecureTrustManagerFactory.INSTANCE)
            .build()
val tcpClient = TcpClient.create().secure { sslProviderBuilder -> sslProviderBuilder.sslContext(sslContext) }
val httpClient = HttpClient.from(tcpClient)
val webClient =  WebClient.builder().clientConnector(ReactorClientHttpConnector(httpClient)).build()
Run Code Online (Sandbox Code Playgroud)

欲获得更多信息:


Fri*_*ing 6

另一种方法,如果你想编写生产代码,就是创建一个这样的spring bean,它使用spring-boot服务器中的信任库和密钥库所在的设置来修改注入的webclient.在客户端中,如果使用2-way-ssl,则只需要提供密钥库.不确定,为什么ssl-stuff没有预配置且易于注入,类似于非常酷的spring-boot服务器设置.

import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
.
.
.

  @Bean
  WebClientCustomizer configureWebclient(@Value("${server.ssl.trust-store}") String trustStorePath, @Value("${server.ssl.trust-store-password}") String trustStorePass,
      @Value("${server.ssl.key-store}") String keyStorePath, @Value("${server.ssl.key-store-password}") String keyStorePass, @Value("${server.ssl.key-alias}") String keyAlias) {

      return (WebClient.Builder webClientBuilder) -> {
          SslContext sslContext;
          final PrivateKey privateKey;
          final X509Certificate[] certificates;
          try {
            final KeyStore trustStore;
            final KeyStore keyStore;
            trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            trustStore.load(new FileInputStream(ResourceUtils.getFile(trustStorePath)), trustStorePass.toCharArray());
            keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(new FileInputStream(ResourceUtils.getFile(keyStorePath)), keyStorePass.toCharArray());
            List<Certificate> certificateList = Collections.list(trustStore.aliases())
                .stream()
                .filter(t -> {
                  try {
                    return trustStore.isCertificateEntry(t);
                  } catch (KeyStoreException e1) {
                    throw new RuntimeException("Error reading truststore", e1);
                  }
                })
                .map(t -> {
                  try {
                    return trustStore.getCertificate(t);
                  } catch (KeyStoreException e2) {
                    throw new RuntimeException("Error reading truststore", e2);
                  }
                })
                .collect(Collectors.toList());
            certificates = certificateList.toArray(new X509Certificate[certificateList.size()]);
            privateKey = (PrivateKey) keyStore.getKey(keyAlias, keyStorePass.toCharArray());
            Certificate[] certChain = keyStore.getCertificateChain(keyAlias);
            X509Certificate[] x509CertificateChain = Arrays.stream(certChain)
                .map(certificate -> (X509Certificate) certificate)
                .collect(Collectors.toList())
                .toArray(new X509Certificate[certChain.length]);
            sslContext = SslContextBuilder.forClient()
                .keyManager(privateKey, keyStorePass, x509CertificateChain)
                .trustManager(certificates)
                .build();

            HttpClient httpClient = HttpClient.create()
                .secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
            ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
            webClientBuilder.clientConnector(connector);
          } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException | UnrecoverableKeyException e) {
            throw new RuntimeException(e);
          }
        };
  }
Run Code Online (Sandbox Code Playgroud)

这里是您使用Webclient的部分:import org.springframework.web.reactive.function.client.WebClient;

@Component
public class ClientComponent {

  public ClientComponent(WebClient.Builder webClientBuilder, @Value("${url}") String url) {
    this.client = webClientBuilder.baseUrl(solrUrl).build();
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我可以测试我的旧代码,问题是,私钥的证书链和信任链之间显然存在差异,即使它包含相同的密钥。我现在发布的代码已经过测试和工作,但是证书链中密钥的排序可能会破坏信任链(至少我看到这种情况发生)。希望现在这对其他人更有帮助。我不知道为什么这个标准用例如此复杂。 (2认同)