访问https地址时出现异常:java.security.cert.CertificateException: No subject alternative names present

前几天在工作中碰到一个问题,我们通过回调接口的形式向对接方返回数据,在工程中使用的HttpClient是Spring自带的RestTemplate。但是有一家给到的回调地址为https://ip地址+uri的形式,在进行回调时抛出了如题异常,我理解为是目标地址没有安全证书导致的。查阅一些资料后知道,原来https访问的时候是需要域名才可以的,无法使用ip。这跟前面的没有安全证书又有什么联系呢?其实,认证服务器使用ip的话。,会出现很多问题,所以不可以使用ip进行证书配置,只能使用域名。所以,使用ip地址的形式自然就没有证书了。

但是,对方表示目前他们提供的是测试地址,先进行测试使用,生产环境再提供域名地址。那么,我就需要想办法去使用ip地址形式的url地址。解决方法很简单,既然抛出异常的原因是请求时没有证书,那么就想办法在请求时忽略掉证书就可以了。通过查阅一些资料,我在工程中使用了如下的方法:

首先,定义一个HttpClientUtils.java类,理解为用来生成RestTemplate的模板,具体内容如下:其中设置了信任所有的证书,并且不会对域名进行检查之类的开放性措施。

public class HttpClientUtils {

    public static CloseableHttpClient acceptsUntrustedCertsHttpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        HttpClientBuilder b = HttpClientBuilder.create();

        // setup a Trust Strategy that allows all certificates.
        //
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                return true;
            }
        }).build();
        b.setSSLContext(sslContext);

        // don't check Hostnames, either.
        //      -- use SSLConnectionSocketFactory.getDefaultHostnameVerifier(), if you don't want to weaken
        HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;

        // here's the special part:
        //      -- need to create an SSL Socket Factory, to use our weakened "trust strategy";
        //      -- and create a Registry, to register it.
        //
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
        Registry socketFactoryRegistry = RegistryBuilder.create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory)
                .build();

        // now, we create connection-manager using our Registry.
        //      -- allows multi-threaded use
        PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager( socketFactoryRegistry);
        connMgr.setMaxTotal(200);
        connMgr.setDefaultMaxPerRoute(100);
        b.setConnectionManager( connMgr);

        // finally, build the HttpClient;
        //      -- done!
        CloseableHttpClient client = b.build();

        return client;
    }

}

然后,就是使用该模板生成对应的RestTemplate实例,我是在SpringBoot中的配置类中使用(如果你使用的是xml配置文件,迁移到配置文件中即可),具体代码如下:

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate httpsRestTemplate(HttpComponentsClientHttpRequestFactory httpsFactory){
        RestTemplate restTemplate = new RestTemplate(httpsFactory);
        restTemplate.setErrorHandler(new ResponseErrorHandler() {
            @Override
            public boolean hasError(ClientHttpResponse clientHttpResponse) {
                return false;
            }

            @Override
            public void handleError(ClientHttpResponse clientHttpResponse) {
                //默认处理非200的返回,会抛异常
            }
        });
        return restTemplate;
    }

    @Bean(name = "httpsFactory")
    public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory() throws Exception{
        CloseableHttpClient httpClient = HttpClientUtils.acceptsUntrustedCertsHttpClient();
        HttpComponentsClientHttpRequestFactory httpsFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
        httpsFactory.setReadTimeout(2000);
        httpsFactory.setConnectTimeout(2000);
        return httpsFactory;
    }

然后在你需要使用的地方,使用@Autowired注入使用即可,生成的RestTemplate在请求https的地址时,会采用开放性的策略,即不用检查证书。

你可能感兴趣的:(Java)