복붙노트

[SPRING] ssl을 사용하는 Spring 5 WebClient

SPRING

ssl을 사용하는 Spring 5 WebClient

WebClient 사용 예제를 찾으려고합니다. 제 목표는 Spring 5 WebClient를 사용하여 https 및 자체 서명 인증서를 사용하여 REST 서비스를 쿼리하는 것입니다.

어떤예요?

해결법

  1. ==============================

    1.확인없이 모든 X.509 인증서 (자체 서명 포함)를 신뢰하는 안전하지 않은 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();
    }
    
  2. ==============================

    2.Spring 5.1.1 (Spring boot 2.1.0)과 같은 모양은 ReactorClientHttpConnector에서 HttpClientOptions를 제거 했으므로 ReactorClientHttpConnector 인스턴스를 생성하는 동안 옵션을 구성 할 수 없습니다.

    Spring 5.1.1 (Spring boot 2.1.0)과 같은 모양은 ReactorClientHttpConnector에서 HttpClientOptions를 제거 했으므로 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()
    

    기본적으로 HttpClient를 만드는 동안 안전하지 않은 sslContext를 구성한 다음이 httpClient를 ReactorClientHttpConnector에서 전역 적으로 사용하도록 전달합니다.

    다른 옵션은 아래 그림과 같이 안전하지 않은 sslContext로 TcpClient를 구성하고이를 사용하여 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()
    

    자세한 내용은:

  3. ==============================

    3.이것을 수정하여 스프링 부트 2.0 -> 2.1 변경을 수용해야했습니다.

    이것을 수정하여 스프링 부트 2.0 -> 2.1 변경을 수용해야했습니다.

    또 다른 방법은, 프로덕션 코드를 프로그래밍하려면, 스프링 부트 서버의 설정을 사용하여 트러스트 스토어와 키 스토어의 위치를 ​​사용하여 삽입 된 웹 클라이언트를 수정하는 것과 같은 스프링 빈을 생성하는 것입니다. 클라이언트에서 2-way-ssl을 사용하는 경우에만 키 스토어를 제공하면됩니다. 확실하지 않은 이유는 정말 멋진 스프링 부트 서버 설정과 마찬가지로 ssl 항목이 미리 구성되어 있고 쉽게 주입 할 수없는 이유입니다.

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

    여기서 Webclient를 사용하는 부분은 다음과 같습니다.     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();
      }
    }
    
  4. from https://stackoverflow.com/questions/45418523/spring-5-webclient-using-ssl by cc-by-sa and MIT license