Spring Boot之 RestTemplate 如何调用 HTTPS 请求

Spring Boot之 RestTemplate 如何调用 HTTPS 请求

1 具体使用

如何构造 RestTemplate,以便可以调用 HTTPS,直接上源码,如下:

@Configuration
public class RestTemplateManager {

    @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) {
            }
        });
        return restTemplate;
    }

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

    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 Host names, either.
        HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;

        // create a SSL Socket Factory, to use our weakened "trust strategy"  and create a Registry, to register it.
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory)
                .build();

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

        return b.build();
    }
}

然后在需要使用的地方,直接注入即可

    @Autowired
    private RestTemplate restTemplate;

2 踩坑

说点踩坑的题外话

2.1 背景

历史原因,服务A需要调用服务A自身的API,此时出现来Bug,但在出现问题前只有一个commit,就是修改了RestTemplate的构造方法(以便可以调用HTTPS格式的URL)。

2.2 排查

排查初期一直很奇怪,调用请求是单例,但是每次debug发现调用的用户都会由用户a变为用户b。
但是用户属性在请求初期是正常的,待到逻辑处理中间才发现是通过请求中的session中变更的。
因为内部调用API是使用RestTemplate,此前没仔细看源码,此时怀疑可能是其复用了链接请求。

可以验证是否请求连接复用:
查看发过来的请求地址和端口是否相等,一般情况下 request 每次的请求端口不同。

 String addr = request.getRemoteAddr() + ":" + request.getRemotePort();

很明显两次是一致的,确定了HTTP请求连接的复用导致session内包含旧的用户名,导致请求的用户和session的用户不同,而出现权限不同,导致创建数据集时出现项目下无可用数据集的报错提示。

2.3 总结

后面也去查看了一下 RestTemplate 源码,确定了猜想,具体信息如下:

RestTemplate 的无参构造方法使用的 ClientHttpRequestFactory 类型实例为:SimpleClientHttpRequestFactory 的createRequest方法内openConnection是每次都新建一个新的连接。具体可见openConnection源码的注释说明。

  * <P>A new instance of {@linkplain java.net.URLConnection URLConnection} is
     * created every time when invoking the
     * {@linkplain java.net.URLStreamHandler#openConnection(URL)
     * URLStreamHandler.openConnection(URL)} method of the protocol handler for
     * this URL.</P>

但为了支持 HTTPS,此处需要RestTemplate(ClientHttpRequestFactory requestFactory) 去构造对HTTPS的兼容处理,而此处需要 引入httpClient,对于此时的RestTemplate,其内部的连接由线程池管理,每次发起的请求会最大化复用闲置的线程,而不是新建一个连接。


版权声明:本文为qq_35033270原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。