在Spring RestTemplate中禁用SSL证书验证


76

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

我想从Web应用程序A到Web应用程序B进行https调用,但是我在计算机B中使用了自签名证书。因此,我的HTTPS请求失败。

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

Answers:


29

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

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

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


4
我正在使用Spring的RestTemplate类,您知道如何在RestTemplate中做到这一点吗?
Prabhu R

7
实际上,这在RestTemplate之外。一些示例代码可在forum.springsource.org/showthread.php?t=60854中获得。另外,也要看看stackoverflow.com/questions/1725863/...
拉古拉姆

16
您在哪里可以解释?
kamaci 2011年


7
这有所帮助,谢谢。顺便说一下,您可以将其简化为这样的lambda表达式HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true);
saidfagan

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

45

从本质上讲,您需要做的两件事是使用信任所有证书自定义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;
}

5
TrustStrategy acceptingTrustStrategy =(X509Certificate [] x509Certificates,String s)-> true;
Humoyun Ahmad

1
谢谢,这救了我一命。其他所有方法(使用keytool,blog.codeleak.pl / 2016/02 /…以及大约5〜6个其他stackoverflow帖子(包括对此问题的答案)将导出的.crt添加到java cacerts中)都花费了我7个小时的时间。答案是我最后的希望,谢谢。
Gyuhyeon Lee

1
NoopHostnameVerifier很重要-其他答案忽略了这一点。一种用例是通过环回来使用您的公共SSL证书localhost
jocull

NoopH​​ostnameVerifier在httpclient-android:4.3.5.1中不可用,如果使用httpclient:4.5.6,则会出现此错误:没有Lorg / apache / http / conn / ssl / AllowAllHostnameVerifier类型的静态字段INSTANCE。有人可以帮我吗?
Celtik

1
请提及支持此解决方案的Apache HTTP客户端库名称和版本,原始问题询问有关AFAICT不会自动将其引入该库的Spring REST模板。
chrisinmtown,

8

用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验证程序=新的NullHostnameVerifier(); 您可以看到记得我cookie是一个字符串
Ran Adler

5

完成代码以禁用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);
    }
});

1
NoopHostnameVerifier不必依赖(Apache)。只需提供的(简单)自定义实现即可HostnameVerifier,如其他答案所示。这甚至可以是lambda,例如:(hostname, session) -> true
布伦特·布拉德本

4

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

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

}

2
这失败,并显示“ sun.security.validator.ValidatorException:PKIX路径构建失败...”,但它也被要求跳过证书检查
socona

1
@socona感谢您指出。如方法名称中所述,这只是关闭了主机名验证。
阿米

3

我找到了一种简单的方法

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

2
哪个进口?很难理解。
sunsoft

我也会添加进口!
Yash Jagdale

1

要推翻默认策略,您可以在连接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一起在aRestTemplateBuilder内有效@Configuration。另外:-我不是使用嵌套类,而是使用了TrustAllStrategy
elonderin

1

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

0

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

正确解决问题的方法

  • 注册SSL证书

解决问题的正确方法

  • 从网站下载损坏的SSL证书
  • 将SSL证书导入Java cacerts(证书存储)

    keytool -importcert -trustcacerts -noprompt -storepass changeit -alias citrix -keystore“ C:\ Program Files \ Java \ jdk-11.0.2 \ lib \ security \ cacerts”-文件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();
        }
    

尽管此代码可以为问题提供解决方案,但强烈建议您提供有关此代码为何和/或如何回答问题的其他上下文。从长远来看,只有代码的答案通常变得毫无用处,因为遇到类似问题的未来查看者将无法理解解决方案背后的原因。
palaѕн

0

如果您正在使用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())
    }

0

HttpClient> 4.3的Java代码示例

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>

您也可以找到HttpClient <4.3的Java代码示例

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.