【问题标题】:Disabling SSL Certificate Validation in Spring RestTemplate在 Spring RestTemplate 中禁用 SSL 证书验证
【发布时间】:2011-05-03 14:56:31
【问题描述】:

我在两台不同的机器上拥有两个基于 Spring 的 Web 应用程序 A 和 B。

我想从网络应用 A 向网络应用 B 进行 HTTPS 调用,但是,我在机器 B 中使用自签名证书。所以我的 HTTPS 请求失败。

在 Spring 中使用 RestTemplate 时如何禁用 HTTPS 证书验证?我想禁用验证,因为 Web 应用 A 和 B 都在内部网络中,但数据传输必须通过 HTTPS 进行

【问题讨论】:

    标签: spring validation ssl-certificate resttemplate


    【解决方案1】:
    @Bean
    public RestTemplate restTemplate() 
                    throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
    
        SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
                        .loadTrustMaterial(null, acceptingTrustStrategy)
                        .build();
    
        SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
    
        CloseableHttpClient httpClient = HttpClients.custom()
                        .setSSLSocketFactory(csf)
                        .build();
    
        HttpComponentsClientHttpRequestFactory requestFactory =
                        new HttpComponentsClientHttpRequestFactory();
    
        requestFactory.setHttpClient(httpClient);
        RestTemplate restTemplate = new RestTemplate(requestFactory);
        return restTemplate;
     }
    

    【讨论】:

      【解决方案2】:

      您需要做的两件事基本上是使用信任所有证书的自定义 TrustStrategy,以及使用 NoopH​​ostnameVerifier() 禁用主机名验证。这是代码,以及所有相关的导入:

      import java.security.KeyManagementException;
      import java.security.KeyStoreException;
      import java.security.NoSuchAlgorithmException;
      import java.security.cert.CertificateException;
      import java.security.cert.X509Certificate;
      import javax.net.ssl.SSLContext;
      import org.apache.http.conn.ssl.NoopHostnameVerifier;
      import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
      import org.apache.http.conn.ssl.TrustStrategy;
      import org.apache.http.impl.client.CloseableHttpClient;
      import org.apache.http.impl.client.HttpClients;
      import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
      import org.springframework.web.client.RestTemplate;
      
      public RestTemplate getRestTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
          TrustStrategy acceptingTrustStrategy = (x509Certificates, s) -> true;
          SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
          SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
          CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
          HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
          requestFactory.setHttpClient(httpClient);
          RestTemplate restTemplate = new RestTemplate(requestFactory);
          return restTemplate;
      }
      

      【讨论】:

      • TrustStrategy acceptingTrustStrategy = (X509Certificate[] x509Certificates, String s) ->true;
      • 谢谢,这救了我的命。所有其他方法(使用 keytool、blog.codeleak.pl/2016/02/… 将导出的 .crt 添加到 java cacerts 以及大约 5~6 个其他 stackoverflow 帖子,包括对此的答案)都失败了,花了我 7 个小时的时间。这个答案是我最后的希望,谢谢。
      • NoopHostnameVerifier 非常重要 - 其他答案忽略了这一点。一个用例是通过环回localhost 使用您的公共 SSL 证书。
      • 请提及支持此解决方案的 Apache HTTP 客户端库名称和版本,原始问题询问 AFAICT 不会自动引入该库的 Spring REST 模板,谢谢。
      【解决方案3】:

      你需要添加的是自定义HostnameVerifier类绕过证书验证并返回true

      HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
          public boolean verify(String hostname, SSLSession session) {
              return true;
          }
      });
      

      这需要适当地放置在您的代码中。

      【讨论】:

      • 我正在使用 Spring 的 RestTemplate 类,你知道我如何在 RestTemplate 中做到这一点吗?
      • 其实这在 RestTemplate 之外。 forum.springsource.org/showthread.php?t=60854 提供了一些示例代码。另请看stackoverflow.com/questions/1725863/…
      • 在哪里可以解释一下?
      • 这有帮助,谢谢。顺便说一句,您可以将其简化为像这样的 lambda 表达式 HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
      • 至于“适当的空间”。由于这是一个静态方法,您可以将其放入一些初始化代码中。例如我把它放到我的类中 extends WebSecurityConfigurerAdapter 里面的 configure 方法
      【解决方案4】:

      用 cookie 添加我的回复:

      public static void main(String[] args) {
           MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
           params.add("username", testUser);
           params.add("password", testPass);
           NullHostnameVerifier verifier = new NullHostnameVerifier(); 
           MySimpleClientHttpRequestFactory requestFactory = new MySimpleClientHttpRequestFactory(verifier , rememberMeCookie);
           ResponseEntity<String> response = restTemplate.postForEntity(appUrl + "/login", params, String.class);
      
           HttpHeaders headers = response.getHeaders();
           String cookieResponse = headers.getFirst("Set-Cookie");
           String[] cookieParts = cookieResponse.split(";");
           rememberMeCookie = cookieParts[0];
           cookie.setCookie(rememberMeCookie);
      
           requestFactory = new  MySimpleClientHttpRequestFactory(verifier,cookie.getCookie());
                restTemplate.setRequestFactory(requestFactory);
      }
      
      
      public class MySimpleClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
      
              private final HostnameVerifier verifier;
              private final String cookie;
      
              public MySimpleClientHttpRequestFactory(HostnameVerifier verifier ,String cookie) {
                  this.verifier = verifier;
                  this.cookie = cookie;
              }
      
              @Override
              protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
                  if (connection instanceof HttpsURLConnection) {
                      ((HttpsURLConnection) connection).setHostnameVerifier(verifier);
                      ((HttpsURLConnection) connection).setSSLSocketFactory(trustSelfSignedSSL().getSocketFactory());
                      ((HttpsURLConnection) connection).setAllowUserInteraction(true);
                      String rememberMeCookie = cookie == null ? "" : cookie; 
                      ((HttpsURLConnection) connection).setRequestProperty("Cookie", rememberMeCookie);
                  }
                  super.prepareConnection(connection, httpMethod);
              }
      
              public SSLContext trustSelfSignedSSL() {
                  try {
                      SSLContext ctx = SSLContext.getInstance("TLS");
                      X509TrustManager tm = new X509TrustManager() {
      
                          public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
                          }
      
                          public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
                          }
      
                          public X509Certificate[] getAcceptedIssuers() {
                              return null;
                          }
                      };
                      ctx.init(null, new TrustManager[] { tm }, null);
                      SSLContext.setDefault(ctx);
                      return ctx;
                  } catch (Exception ex) {
                      ex.printStackTrace();
                  }
                  return null;
              }
      
          }
      
      
          public class NullHostnameVerifier implements HostnameVerifier {
                 public boolean verify(String hostname, SSLSession session) {
                    return true;
                 }
              }
      

      【讨论】:

      • NullHostnameVerifier 验证器 = new NullHostnameVerifier();你可以看到记住我 cookie 是一个字符串
      【解决方案5】:

      您可以将它与 HTTPClient API 一起使用。

      public RestTemplate getRestTemplateBypassingHostNameVerifcation() {
          CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
          HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
          requestFactory.setHttpClient(httpClient);
          return new RestTemplate(requestFactory);
      
      }
      

      【讨论】:

      • 失败并显示“sun.security.validator.ValidatorException: PKIX path building failed...”,但它也被要求跳过证书检查
      • @socona 感谢您的指出。如方法名称中所述,这只是关闭主机名验证。
      【解决方案6】:

      禁用 SSL 主机名验证器的完整代码,

      RestTemplate restTemplate = new RestTemplate();
      //to disable ssl hostname verifier
      restTemplate.setRequestFactory(new SimpleClientHttpRequestFactory() {
         @Override
          protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
              if (connection instanceof HttpsURLConnection) {
                  ((HttpsURLConnection) connection).setHostnameVerifier(new NoopHostnameVerifier());
              }
              super.prepareConnection(connection, httpMethod);
          }
      });
      

      【讨论】:

      • 不需要依赖 (Apache) NoopHostnameVerifier。只需提供HostnameVerifier 的(简单)自定义实现,如其他答案所示。这甚至可以是lambda,例如:(hostname, session) -&gt; true。
      • 这个junit怎么写?
      【解决方案7】:

      我找到了一个简单的方法

          TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
          SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
          SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
          CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
          HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
          requestFactory.setHttpClient(httpClient);
      
          RestTemplate restTemplate = new RestTemplate(requestFactory);
      

      使用的进口商品

      import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
      import org.apache.http.conn.ssl.TrustStrategy;
      import org.apache.http.impl.client.CloseableHttpClient;
      import org.apache.http.impl.client.HttpClients;
      import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
      import org.springframework.web.client.RestTemplate;
      import javax.net.ssl.SSLContext;
      import java.security.KeyManagementException;
      import java.security.KeyStoreException;
      import java.security.NoSuchAlgorithmException;
      import java.security.cert.X509Certificate;
      

      【讨论】:

      • 要导入哪个?太难理解了。
      • 我也会添加导入!!
      • 两年了,什么时候加进口
      • 错过了,现在添加了导入。
      【解决方案8】:

      要否决默认策略,您可以在连接 restTemplate 的类中创建一个简单的方法:

       protected void acceptEveryCertificate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
      
          TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
              @Override
              public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                  return true;
              }
          };
      
          restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(
                  HttpClientBuilder
                          .create()
                          .setSSLContext(SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build())
                          .build()));
      }
      

      注意:您当然需要处理异常,因为这种方法只会进一步抛出异常!

      【讨论】:

      • 这对我来说适用于 SB 2.2.4 与 RestTemplateBuilder 中的 @Configuration 相关联。加: - 而不是嵌套类,我使用TrustAllStrategy
      • 这对我有用谢谢 :)
      【解决方案9】:

      此问题与 SSL 连接有关。当您尝试连接到某些资源时,https 协议需要创建安全连接。这意味着只有您的浏览器和网站服务器才知道请求正文中发送了哪些数据。这种安全性是通过存储在网站上并由您的浏览器(或任何其他客户端,在我们的例子中带有 Apache Http 客户端的 Spring RestTemplate)下载的 ssl 证书实现的,并首先连接到主机。有 RSA256 加密和许多其他很酷的东西。但最终:如果证书未注册或无效,您将看到证书错误(HTTPS 连接不安全)。要修复证书错误,网站提​​供商需要为特定网站购买它或以某种方式修复,例如https://www.register.com/ssl-certificates

      解决问题的正确方法

      • 注册 SSL 证书

      解决问题的方法不是正确的

      • download broken SSL certificate from website
      • 将 SSL 证书导入 Java cacerts(证书存储)

        keytool -importcert -trustcacerts -noprompt -storepass changeit -alias citrix -keystore "C:\Program Files\Java\jdk-11.0.2\lib\security\cacerts" -file citrix.cer

      如何解决问题的肮脏(不安全)方式

      • 使 RestTemplate 忽略 SSL 验证

        @Bean
        public RestTemplateBuilder restTemplateBuilder(@Autowired SSLContext sslContext) {
            return new RestTemplateBuilder() {
                @Override
                public ClientHttpRequestFactory buildRequestFactory() {
                    return new HttpComponentsClientHttpRequestFactory(
                            HttpClients.custom().setSSLSocketFactory(
                                    new SSLConnectionSocketFactory(sslContext
                                            , NoopHostnameVerifier.INSTANCE)).build());
                }
            };
        }
        
        @Bean
            public SSLContext insecureSslContext() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
               return SSLContexts.custom()
                        .loadTrustMaterial(null, (x509Certificates, s) -> true)
                        .build();
            }
        

      【讨论】:

      • 虽然此代码可能提供问题的解决方案,但强烈建议您提供有关此代码为何和/或如何回答问题的附加上下文。从长远来看,只有代码的答案通常会变得毫无用处,因为未来遇到类似问题的观众无法理解解决方案背后的原因。
      • 感谢您花时间解释事情!
      【解决方案10】:

      另一种方法非常简单,无需导入任何 APACHE 或任何未知包。

      import javax.net.ssl.HttpsURLConnection;
      import javax.net.ssl.SSLContext;
      import javax.net.ssl.TrustManager;
      import javax.net.ssl.X509TrustManager;
      
          private void ignoreCertificates() {
          TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
              @Override
              public X509Certificate[] getAcceptedIssuers() {
                  return null;
              }
      
              @Override
              public void checkClientTrusted(X509Certificate[] certs, String authType) {
              }
      
              @Override
              public void checkServerTrusted(X509Certificate[] certs, String authType) {
              }
          } };
      
          try {
              SSLContext sc = SSLContext.getInstance("TLS");
              sc.init(null, trustAllCerts, new SecureRandom());
              HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
          } catch (Exception e) {
           
      
          }
      

      并在 RestTemplate 之前设置 ignoreCertificates():

         ignoreCertificates();
         RestTemplate restTemplate = new RestTemplate();
      

      【讨论】:

      • 为我工作。谢谢!
      【解决方案11】:

      安全性:禁用 https/TLS 证书主机名检查,以下代码在 Spring Boot Rest 模板中工作

      *HttpsURLConnection.setDefaultHostnameVerifier(
              //SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
              // * @deprecated (4.4) Use {@link org.apache.http.conn.ssl.NoopHostnameVerifier}
              new NoopHostnameVerifier()
      );*
      

      【讨论】:

        【解决方案12】:

        Java code example for HttpClient > 4.3

        package com.example.teocodownloader;
        
        import org.apache.http.conn.ssl.NoopHostnameVerifier;
        import org.apache.http.impl.client.CloseableHttpClient;
        import org.apache.http.impl.client.HttpClients;
        import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
        import org.springframework.web.client.RestTemplate;
        
        public class Example {
            public static void main(String[] args) {
                CloseableHttpClient httpClient
                        = HttpClients.custom()
                        .setSSLHostnameVerifier(new NoopHostnameVerifier())
                        .build();
                HttpComponentsClientHttpRequestFactory requestFactory
                        = new HttpComponentsClientHttpRequestFactory();
                requestFactory.setHttpClient(httpClient);
                RestTemplate restTemplate = new RestTemplate(requestFactory);
            }
        }
        

        对了,不要忘记在pom文件中添加以下依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        

        你可以找到Java code example for HttpClient < 4.3 as well。

        【讨论】:

          【解决方案13】:

          如果你使用的是rest模板,你可以使用这段代码

              fun getClientHttpRequestFactory(): ClientHttpRequestFactory {
                  val timeout = envTimeout.toInt()
                  val config = RequestConfig.custom()
                      .setConnectTimeout(timeout)
                      .setConnectionRequestTimeout(timeout)
                      .setSocketTimeout(timeout)
                      .build()
          
                  val acceptingTrustStrategy = TrustStrategy { chain: Array<X509Certificate?>?, authType: String? -> true }
          
                  val sslContext: SSLContext = SSLContexts.custom()
                      .loadTrustMaterial(null, acceptingTrustStrategy)
                      .build()
          
                  val csf = SSLConnectionSocketFactory(sslContext)
          
                  val client = HttpClientBuilder
                      .create()
                      .setDefaultRequestConfig(config)
                      .setSSLSocketFactory(csf)
                      .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                      .build()
                  return HttpComponentsClientHttpRequestFactory(client)
              }
          
              @Bean
              fun getRestTemplate(): RestTemplate {
                  return RestTemplate(getClientHttpRequestFactory())
              }
          

          【讨论】:

            【解决方案14】:
            @Bean
            public RestTemplate restTemplate() throws Exception {
                SSLContext sslContext = new SSLContextBuilder()
                        .loadKeyMaterial(keyStore, keyStorePassword.toCharArray(), keyStorePassword.toCharArray())
                        .loadTrustMaterial(trustStore, trustStorePassword.toCharArray(), (cert, authType) -> sslTrustStrategy)
                        .build();
                HostnameVerifier hostnameVerifier = sslTrustStrategy ? new NoopHostnameVerifier() :
                        SSLConnectionSocketFactory.getDefaultHostnameVerifier();
                SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
                HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslSocketFactory).build();
                HttpComponentsClientHttpRequestFactory httpComponentsHttpClientFactory =
                        new HttpComponentsClientHttpRequestFactory(httpClient);
                RestTemplate restTemplate = new RestTemplate(httpComponentsHttpClientFactory);
                return restTemplate;
            }
            

            如果 sslTrustStrategy = true,

            1. 由于 (cert, authType) 信任所有证书 -> sslTrustStrategy
            2. 不要只信任证书中的主机,而是信任所有主机(由于 NoopH​​ostnameVerifier,否则仅信任证书中的主机)

            【讨论】:

              猜你喜欢
              • 2014-06-23
              • 2019-06-28
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-02-14
              • 2018-11-04
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多