【问题标题】:Access HTTPS RESTful service using Web Client in Spring Boot 2.0 throwing exception在 Spring Boot 2.0 中使用 Web Client 访问 HTTPS RESTful 服务抛出异常
【发布时间】:2020-08-06 00:20:15
【问题描述】:

我必须访问一个 https REST Web 服务(https://example.com),客户端已为其提供了包含 2 个 .cer 文件的证书。

我创建了keystore.jkstruststore.jks 文件和imported the .cer files to jks file。通过使用以下命令

keytool -genkeypair -alias abcd -keyalg RSA -sigalg garegar -keysize 2048 -storetype jks -keystore keystore.jks -validity 365 -storepass changeit

keytool -import -alias abcd -trustcacerts -file free/ca_bundle.cer -keystore keystore.jksccessfull

我已将所有属性添加到 application.yml

   truststore-location: 
   keystore-location: 
   truststore-password: 
   keystore-password: 
   key-alias:

在配置类中创建了 bean 来获取 webclient 对象。

@Data
@NoArgsConstructor
@AllArgsConstructor
@Configuration
public class WebServiceRestConfig {

    @Value(value = "${wcc.rest.endpoint}")
    private String url;

    @Value(value = "${wcc.dam.username}")
    private String username;

    @Value(value = "${wcc.dam.password}")
    private String password;

    @Value(value = "${ssl.truststore-location}")
    private String trustStore;

    @Value(value = "${ssl.keystore-location}")
    private String keyStore;

    @Value(value = "${ssl.truststore-password}")
    private String trustStorePassword;

    @Value(value = "${ssl.keystore-password}")
    private String keyStorePassword;

    @Value(value = "${ssl.key-alias}")
    private String keyAlias;




    @Bean
    WebClientCustomizer configureWebclient() {

        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(getTrustStore())),
                        trustStorePassword.toCharArray());
                keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
                keyStore.load(new FileInputStream(ResourceUtils.getFile(getKeyStore())), keyStorePassword.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, keyStorePassword.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, keyStorePassword, x509CertificateChain)
                        .trustManager(certificates).build();

                HttpClient httpClient = HttpClient.create()
                        .secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
                ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
                webClientBuilder.clientConnector(connector).baseUrl(getUrl()).build();

            } catch (KeyStoreException e) {
                throw new RuntimeException(e);
            }catch(CertificateException e) {
                throw new RuntimeException(e);
            }catch(NoSuchAlgorithmException e) {
                throw new RuntimeException(e);
            }catch(IOException e) {
                throw new RuntimeException(e);
            }catch(UnrecoverableKeyException e) {
                throw new RuntimeException(e);
            }
        };
    }

}

但是当我运行应用程序时,我遇到了异常

SEVERE: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is reactor.core.Exceptions$ReactiveException: javax.net.ssl.SSLHandshakeException: General SSLEngine problem] with root cause
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:145)
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
    at sun.security.validator.Validator.validate(Validator.java:260)
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:281)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:136)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1465)
    at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:212)
    at sun.security.ssl.Handshaker.processLoop(Handshaker.java:969)

现在我完全不知道该做什么以及如何解决这个问题我需要在资源文件夹中添加任何东西还是我在这里遗漏了什么?

【问题讨论】:

    标签: spring-boot spring-security ssl-certificate java-security spring-webclient


    【解决方案1】:

    我也遇到过类似的问题:

    @Bean("oAuth2")
    public WebClient webOAuth2Client(ReactiveClientRegistrationRepository registration,
                               ServerOAuth2AuthorizedClientRepository clientRepository) {
    
        ServerOAuth2AuthorizedClientExchangeFilterFunction filter =
                new ServerOAuth2AuthorizedClientExchangeFilterFunction(
                        registration,
                        clientRepository);
        filter.setDefaultClientRegistrationId("oAuth2");
       
    
        return WebClient
                .builder()
                .filter(filter)
                .build();
    }
    

    在属性文件中类似于:

        security:
        oauth2:
          client:
            provider:
              oAuth2:
                token-uri: url:port/auth/realms/SCRealm/protocol/openid-connect/token
            registration:
              oAuth2:
                clientId: client_id
                clientSecret: client_secret
                authorizationGrantType: client_credentials
                clientAuthenticationMethod: post
    

    在主应用程序类中:

    System.setProperty("-Dio.netty.handler.ssl.noOpenSsl","true");
    System.setProperty("javax.net.ssl.trustStore", "keyStore.jks");
    System.setProperty("javax.net.ssl.trustStoreType", "JKS");
    System.setProperty("javax.net.ssl.trustStorePassword", "any_strong_pass");
    

    【讨论】:

      猜你喜欢
      • 2017-06-15
      • 2019-05-28
      • 2012-03-06
      • 1970-01-01
      • 2020-06-02
      • 2018-10-25
      • 1970-01-01
      • 1970-01-01
      • 2018-12-12
      相关资源
      最近更新 更多