在 Spring RestTemplate 中禁用 SSL 证书验证

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4072585/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 04:11:16  来源:igfitidea点击:

Disabling SSL Certificate Validation in Spring RestTemplate

springvalidationssl-certificateresttemplate

提问by Prabhu R

I am having two Spring based web apps A and B, on two different machines.

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

I want to make a https call from web app A to web app B, however I am using a self-signed certificate in Machine B. So my HTTPS request fails.

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

How can I disable https certificate validation when using RestTemplate in Spring ? I want to disable validation because both web app A and B are within the internal network, but data transfer has to happen over HTTPS

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

采纳答案by Raghuram

What you need to add is a custom HostnameVerifierclass bypasses certificate verification and returns true

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

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

This needs to be placed appropriately in your code.

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

回答by DanieleDM

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

回答by rouble

Essentially two things you need to do are use a custom TrustStrategy that trusts all certs, and also use NoopHostnameVerifier()to disable hostname verification. Here is the code, with all the relevant imports:

基本上你需要做的两件事是使用信任所有证书自定义 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 = new TrustStrategy() {
        @Override
        public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
            return 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;
}

回答by Ran Adler

Add my response with cookie :

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

回答by Amit Parashar

You can use this with HTTPClient API.

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

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

}

回答by user1986251

Complete code to disable SSL hostname verifier,

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

回答by sagaris

To overrule the default strategy you can create a simple method in the class where you are wired your restTemplate:

要推翻默认策略,您可以在连接 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()));
}

Note: Surely you need to handle exceptions since this method only throws them further!

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

回答by Sats

Security: disable https/TLS certificate hostname check,the following code worked in spring boot rest template

安全性:禁用 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()
);*

回答by Yash Jagdale

I found a simple way

我找到了一个简单的方法

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

回答by Oleg Maksymuk

This problem is about SSL connection. When you try to connect to some resource https protocol requires to create secured connection. That means only your browser and website server know what data is being sent in requests bodies. This security is achieved by ssl certificates that stored on website and are being downloaded by your browser (or any other client, Spring RestTemplate with Apache Http Client behind in our case) with first connection to host. There are RSA256 encryption and many other cool things around. But in the end of a day: In case certificate is not registered or is invalid you will see certificate error (HTTPS connection is not secure). To fix certificate error website provider need to buy it for particular website or fix somehow e.g. https://www.register.com/ssl-certificates

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

Right way how problem can be solved

解决问题的正确方法

  • Register SSL certificate
  • 注册 SSL 证书

Not a right way how problem can be solved

解决问题的方法不对

  • download broken SSL certificate from website
  • import SSL certificate to Java cacerts (certificate storage)

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

  • 从网站下载损坏的 SSL 证书
  • 将 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

Dirty (Insecure) way how problem can be solved

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

  • make RestTemplate to ignore SSL verification

    @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();
        }
    
  • 使 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();
        }