Java 使用restTemplate时需要忽略证书

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36672803/
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-08-11 18:33:47  来源:igfitidea点击:

Need to ignore certificate when using restTemplate

javaspring-mvcsslresttemplate

提问by Daniel Newtown

I am trying to send a request to following address. The certificate is not valid and I would like to ignore it. I wrote following code based on my research on 1, 2but I am not able to complete it. I am using Java 1.7,

我正在尝试向以下地址发送请求。证书无效,我想忽略它。我根据对12 的研究编写了以下代码,但无法完成。我使用的是 Java 1.7,

https://api.stubhubsandbox.com/search/catalog/events/v3

Code

代码

private static final TrustManager[] UNQUESTIONING_TRUST_MANAGER = new TrustManager[]{
    new X509TrustManager() {
        public java.security.cert.X509Certificate[] getAcceptedIssuers(){
            return null;
        }
        public void checkClientTrusted( X509Certificate[] certs, String authType ){}
        public void checkServerTrusted( X509Certificate[] certs, String authType ){}
        public void checkClientTrusted(
                java.security.cert.X509Certificate[] arg0, String arg1)
                throws CertificateException {
            // TODO Auto-generated method stub

        }
        public void checkServerTrusted(
                java.security.cert.X509Certificate[] arg0, String arg1)
                throws CertificateException {
            // TODO Auto-generated method stub

        }
    }
};

public static void main(String[] args) {
    TrustStrategy acceptingTrustStrategy = 

    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);
    String url = "https://api.stubhubsandbox.com/search/catalog/events/v3";
    RestTemplate rest = new RestTemplate();
    Map<String, String> mvm = new HashMap<String, String>();
    mvm.put("Authorization", "Bearer TOKEEEEEEEN");
    Object object = rest.postForObject(url, null, Object.class, mvm);
    System.err.println("done");


}

回答by chrisl08

Not sure if things have changed after jdk6, but last time I was trying to do this we needed to import the SSL certificate to the keystore of the JAVA_HOME used to run the programs utilizing the trusted ssl.

不确定在 jdk6 之后情况是否发生了变化,但上次我尝试这样做时,我们需要将 SSL 证书导入到 JAVA_HOME 的密钥库中,该密钥库用于使用受信任的 ssl 运行程序。

First, you will need to export the certificate to a file. In windows, you can use any browser to save the SSL certificate to your personal certificates store and then run mmc, add certificates snapin (File/Add Remove Snapin) and save the certificate to disk.

首先,您需要将证书导出到文件中。在 Windows 中,您可以使用任何浏览器将 SSL 证书保存到您的个人证书存储区,然后运行 ​​mmc,添加证书管理单元(File/Add Remove Snapin)并将证书保存到磁盘。

Then you need to import the certificate to trusted domain cacerts using the keytool. But you need to import it to the keystore that your java_home uses when running your programs above.

然后您需要使用keytool将证书导入受信任的域 cacerts 。但是您需要将它导入到您的 java_home 在运行上面的程序时使用的密钥库。

The command below will add certificate file "mycertificate.cer" to keystore in file "cacerts.jks". The alias is "webservice" :

下面的命令会将证书文件“mycertificate.cer”添加到文件“cacerts.jks”中的密钥库。别名是“webservice”:

"%JAVA_HOME%\bin\keytool" -import -trustcacerts -alias webservice -file mycertificate.cer -keystore cacerts.jks

Usually, the Keystore password is "changeit", no quotes. Change it for production use

通常,密钥库密码是“changeit”,没有引号。将其更改为生产用途

回答by Costi Ciudatu

As you may have noticed, Spring's RestTemplatedelegates all the HTTP(S) related stuff to the underlying implementation of ClientHttpRequestFactory. Since you're using the HttpClient-based implementation, here are a couple of useful SO links on how to achieve this for the internal HttpClient:

您可能已经注意到,SpringRestTemplate将所有与 HTTP(S) 相关的内容委托给ClientHttpRequestFactory. 由于您使用的是HttpClient-based 实现,这里有几个有用的 SO 链接,说明如何为内部实现这一点HttpClient

Apparently, since version 4.4, this can be done as:

显然,从 4.4 版开始,这可以通过以下方式完成:

CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).build();

回答by mika

To bypass SSL checks in several spring projects I always re-use a SSLUtils class I wrote (or found) some time ago in conjunction with spring's RestTemplate. Using the class provided below you just need to call the static SSLUtil.turnOffSslChecking()method before you send your request.

为了在几个 spring 项目中绕过 SSL 检查,我总是重新使用我前段时间编写(或找到)的 SSLUtils 类,并结合 spring 的 RestTemplate。使用下面提供的类,您只需要SSLUtil.turnOffSslChecking()在发送请求之前调用静态方法。

import javax.net.ssl.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public final class SSLUtil{

    static {
        //for localhost testing only
        javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
        new javax.net.ssl.HostnameVerifier(){

            public boolean verify(String hostname,
                    javax.net.ssl.SSLSession sslSession) {
                if (hostname.equals("localhost")) {
                    return true;
                }
                return false;
            }
        });
    }

    private static final TrustManager[] UNQUESTIONING_TRUST_MANAGER = new TrustManager[]{
            new X509TrustManager() {
                public java.security.cert.X509Certificate[] getAcceptedIssuers(){
                    return null;
                }
                public void checkClientTrusted( X509Certificate[] certs, String authType ){}
                public void checkServerTrusted( X509Certificate[] certs, String authType ){}
            }
        };

    public  static void turnOffSslChecking() throws NoSuchAlgorithmException, KeyManagementException {
        // Install the all-trusting trust manager
        final SSLContext sc = SSLContext.getInstance("SSL");
        sc.init( null, UNQUESTIONING_TRUST_MANAGER, null );
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    }

    public static void turnOnSslChecking() throws KeyManagementException, NoSuchAlgorithmException {
        // Return it to the initial state (discovered by reflection, now hardcoded)
        SSLContext.getInstance("SSL").init( null, null, null );
    }

    private SSLUtil(){
        throw new UnsupportedOperationException( "Do not instantiate libraries.");
    }
}

Give it a try. Hope this works and turns out as an easy solution for you.

试一试。希望这行得通,并且对您来说是一个简单的解决方案。

回答by DanieleDM

You can use this code:

您可以使用此代码:

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

in java 7 replace lambda expression with:

在 Java 7 中,将 lambda 表达式替换为:

        TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
        @Override public boolean isTrusted(X509Certificate[] x509Certificates, String s)
                        throws CertificateException {
            return true;
        }
    };

回答by Novice

Add the SSLContextand X509TrustManagerand the HostnameVerifierinstances to the http ClientBuilders. They can be for instance (given my example)

SSLContextX509TrustManager以及HostnameVerifier实例添加到 http ClientBuilders。例如,它们可以是(以我的示例为例)

  1. HttpClientBuilder with HttpComponentsClientHttpRequestFactory
  2. OkHttpClient.Builder with OkHttp3ClientHttpRequestFactory
  1. 带有 HttpComponentsClientHttpRequestFactory 的 HttpClientBuilder
  2. OkHttpClient.Builder 和 OkHttp3ClientHttpRequestFactory

Here's the sample code for Apache HttpClient & OkHttpClient. Its for demo purpose but you can use it

这是 Apache HttpClient 和 OkHttpClient 的示例代码。它用于演示目的,但您可以使用它

Apache HttpClient

Apache HttpClient

RestTemplate restTemplate = new RestTemplate(SSLClientFactory.getClientHttpRequestFactory(HttpClientType.HttpClient));

and OkHttpClient

和 OkHttpClient

RestTemplate restTemplate = new RestTemplate(SSLClientFactory.getClientHttpRequestFactory(HttpClientType.OkHttpClient));

The SSLClientFactory is custom class here

SSLClientFactory 是这里的自定义类

import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;

import okhttp3.OkHttpClient;

public abstract class SSLClientFactory {

    private static boolean allowUntrusted = false;
    private static final long LOGIN_TIMEOUT_SEC = 10;
    private static HttpClientBuilder closeableClientBuilder = null;
    private static OkHttpClient.Builder okHttpClientBuilder = null;

    public enum HttpClientType{
        HttpClient,
        OkHttpClient
    } 


    public static synchronized ClientHttpRequestFactory getClientHttpRequestFactory(HttpClientType httpClientType){

        ClientHttpRequestFactory requestFactory = null;

        SSLContext sslContext = SSLClientFactory.getSSlContext();

        if(null == sslContext){
            return requestFactory;
        }

        switch (httpClientType) {

        case HttpClient:
            closeableClientBuilder = HttpClientBuilder.create();

            //Add the SSLContext and trustmanager
            closeableClientBuilder.setSSLContext(getSSlContext());
            //add the hostname verifier
            closeableClientBuilder.setSSLHostnameVerifier(gethostnameVerifier());   

            requestFactory = new HttpComponentsClientHttpRequestFactory(closeableClientBuilder.build());

            break;
        case OkHttpClient:
            okHttpClientBuilder = new OkHttpClient().newBuilder().readTimeout(LOGIN_TIMEOUT_SEC, TimeUnit.SECONDS);

            //Add the SSLContext and trustmanager
            okHttpClientBuilder.sslSocketFactory(getSSlContext().getSocketFactory(), getTrustManager());
            //add the hostname verifier
            okHttpClientBuilder.hostnameVerifier( gethostnameVerifier());

            requestFactory = new OkHttp3ClientHttpRequestFactory(okHttpClientBuilder.build());

            break;
        default:
            break;
        }

        return requestFactory;

    }


    private static SSLContext getSSlContext(){



        final TrustManager[] trustAllCerts = new TrustManager[]{getTrustManager()};

        SSLContext sslContext = null;
        try {

            sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        }



        return sslContext;

    }

    private static X509TrustManager getTrustManager(){

        final X509TrustManager trustManager = new X509TrustManager() {

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                X509Certificate[] cArrr = new X509Certificate[0];
                return cArrr;
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                // TODO Auto-generated method stub

            }

            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                // TODO Auto-generated method stub

            }
        };

        return trustManager;
    }

    private static HostnameVerifier gethostnameVerifier(){

        HostnameVerifier hostnameVerifier = new HostnameVerifier() {

            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }
        };

        return hostnameVerifier;

    }

}

回答by Brijesh Patel

If you are using Apache httpClient 4.5 following:

如果您使用的是 Apache httpClient 4.5,请执行以下操作:

public static void main(String... args)  {

    try (CloseableHttpClient httpclient = createAcceptSelfSignedCertificateClient()) {
        HttpGet httpget = new HttpGet("https://example.com");
        System.out.println("Executing request " + httpget.getRequestLine());

        httpclient.execute(httpget);
        System.out.println("----------------------------------------");
    } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException | IOException e) {
        throw new RuntimeException(e);
    }
}

private static CloseableHttpClient createAcceptSelfSignedCertificateClient()
        throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {

    // use the TrustSelfSignedStrategy to allow Self Signed Certificates
    SSLContext sslContext = SSLContextBuilder
            .create()
            .loadTrustMaterial(new TrustSelfSignedStrategy())
            .build();

    // we can optionally disable hostname verification. 
    // if you don't want to further weaken the security, you don't have to include this.
    HostnameVerifier allowAllHosts = new NoopHostnameVerifier();

    // create an SSL Socket Factory to use the SSLContext with the trust self signed certificate strategy
    // and allow all hosts verifier.
    SSLConnectionSocketFactory connectionFactory = new SSLConnectionSocketFactory(sslContext, allowAllHosts);

    // finally create the HttpClient using HttpClient factory methods and assign the ssl socket factory
    return HttpClients
            .custom()
            .setSSLSocketFactory(connectionFactory)
            .build();
}

回答by UDIT JOSHI

 @Bean
       public RestTemplate getRestTemplate() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException {
            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);
          return new RestTemplate(requestFactory);
       }

This code bypasses the certificate validation and you can connect with an insecure way by accepting all hosts and certificates. This code works for me

此代码绕过证书验证,您可以通过接受所有主机和证书以不安全的方式进行连接。这段代码对我有用

回答by Ganesh Bhattachan

SSLUtils solution posted by @Sebastián Ezquerro is spot on. I tested this both with RestTemplate and FeignClient - works like a champ. Many thanks to all contributors. In case, you are wondering Feign client solution, here it is:

@Sebastián Ezquerro 发布的 SSLUtils 解决方案就在现场。我用 RestTemplate 和 FeignClient 对此进行了测试 - 像冠军一样工作。非常感谢所有贡献者。如果您想知道 Feign 客户端解决方案,这里是:

    @Bean
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        BasicAuthRequestInterceptor auth = new BasicAuthRequestInterceptor(username,  password);
        RequestTemplate template = new RequestTemplate();
        template.header(HttpHeaders.ACCEPT, "application/json");
        template.header(HttpHeaders.CONTENT_TYPE, "application/json");
        auth.apply(template);

       // disable SSL self signed certificate check
       try {
           SSLUtil.turnOffSslChecking();
        } catch (NoSuchAlgorithmException e) {
           log.error("Error disabling SSL check", e);
        } catch (KeyManagementException e) {
          log.error("Error disabling SSL check", e);
        }
        return auth;
    }