RestTemplate 使用總結
場景:
認證服務器需要有個 http client 把前端發來的請求轉發到 backend service, 然后把 backend service 的結果再返回給前端,服務器本身只做認證功能。
遇到的問題:
-
長連接以保證高性能。RestTemplate 本身也是一個 wrapper 其底層默認是 SimpleClientHttpRequestFactory ,如果要保證長連接, HttpComponentsClientHttpRequestFactory 是個更好的選擇,它不僅可以控制能夠建立的連接數還能細粒度的控制到某個 server 的連接數,非常方便。在默認情況下,RestTemplate 到某個 server 的最大連接數只有 2, 一般需要調的更高些,最好等於 server 的 CPU 個數
-
access_token 不應傳到 backend service. backend service 之間通信不需要 token,因為到這些服務的請求都是已經認證過的,是可信賴的用戶發出的請求。因此轉發請求時要把 parameter 從 request url 中刪掉。刪除 parameter 說難不難,說簡單其實還有點麻煩,網上有一個 UrlEncodedQueryString 可以參考下,它封裝了很多函數,其中就包括從url 中摘掉指定 header
-
請求的 HttpMethod 問題。 HttpMethod 有很多種,http client 不應該對每種 Http method 都單獨處理,所以應選用 RestTemplate 的 exchange 方法。exchange 方法要求給出 RequestBody 參數,而對於 Get 請求,這部分往往為空,所以我們要在 controller 中聲明 @RequestBody(required = false) String body
-
exchange 的返回值和 controller 的返回值。Restful API 一般都是返回 json 的,所以最簡單的是 exchange 和 controller 直接返回 String,但是返回 String 會有很多問題: 首先是如果某些 API 返回的是圖片,那么這個 client 就傻掉了,需要為圖片接口專門寫 API,此外如果 backend service 返回的是 Gzip,那么此 client 必須對 gzip 先解壓縮再返回請求者,如果不解壓縮的話,相當於對着 gzip 數據做了到 String 類型的強制轉換,使得請求者拿到的數據無法解析,所以最好的返回值是 byte[]。對於那種比較大的 json 返回值,省去了對 String 的類型轉換后還能帶來很大的性能提升
-
關於返回值是 byte[] 還是 ResponseEntity<byte[]> 的問題。我覺得還是 ResponseEntity<byte[]> 好些,因為它就是 backend service 的結果。如果返回 byte[] 的話,還要對 HttpServletResponse 的 Header 進行修改,設置 Content-type, Content-encoding 等等。
https://www.cnblogs.com/xinsheng/p/5546221.html
Spring Boot忽略https證書:No subject alternative names present
springboot--resttemplate訪問https請求
<!--http請求包--> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.3</version> </dependency>
import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; 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.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.ssl.SSLContextBuilder; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; 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<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>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; } }
SpringBoot啟動類添加配置
在啟動類上配置RestTemplate(因為啟動類也是配置類,比較方便)
@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(40000); httpsFactory.setConnectTimeout(40000); return httpsFactory; }
不出意外就可以愉快的訪問https請求了!
---------------------
作者:別浪呀
來源:CSDN
原文:https://blog.csdn.net/adminBfl/article/details/84819735
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
https://stackoverflow.com/questions/17619871/access-https-rest-service-using-spring-resttemplate
前面我們介紹了如何使用Apache的HttpClient發送HTTP請求,這里我們介紹Spring的Rest客戶端(即:RestTemplate)
如何發送HTTP、HTTPS請求。注:HttpClient如何發送HTTPS請求,有機會的話也會再給出示例。
聲明:本人一些內容摘錄自其他朋友的博客,鏈接在本文末給出!
基礎知識
微服務都是以HTTP接口的形式暴露自身服務的,因此在調用遠程服務時就必須使用HTTP客戶端。我們可以使用JDK原生的URLConnection、Apache的Http Client、Netty的異步HTTP Client,最方便、最優雅的Feign, Spring的RestTemplate等。
RestTemplate簡述
RestTemplate是Spring提供的用於訪問Rest服務(Rest風格、Rest架構)的客戶端。
RestTemplate提供了多種便捷訪問遠程Http服務的方法,能夠大大提高客戶端的編寫效率。
調用RestTemplate的默認構造函數,RestTemplate對象在底層通過使用java.net包下的實現創建HTTP 請求;我們也可以通過使用ClientHttpRequestFactory指定不同的請求方式:
ClientHttpRequestFactory接口主要提供了兩種實現方式:
1.常用的一種是SimpleClientHttpRequestFactory,使用J2SE提供的方式(既java.net包提供的方式)創建底層
的Http請求連接。
2.常用的另一種方式是使用HttpComponentsClientHttpRequestFactory方式,底層使用HttpClient訪問遠程的
Http服務,使用HttpClient可以配置連接池和證書等信息。
軟硬件環境: Windows10、Eclipse、JDK1.8、SpringBoot
准備工作:引入相關依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
HTTP之GET請求(示例)
import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.client.RestTemplate; import com.google.gson.Gson; /** * 單元測試 * * @author JustryDeng * @DATE 2018年9月7日 下午6:37:05 */ @RunWith(SpringRunner.class) @SpringBootTest public class AbcHttpsTestApplicationTests { /** * RestTemplate 發送 HTTP GET請求 --- 測試 * @throws UnsupportedEncodingException * * @date 2018年7月13日 下午4:18:50 */ @Test public void doHttpGetTest() throws UnsupportedEncodingException { // -------------------------------> 獲取Rest客戶端實例 RestTemplate restTemplate = new RestTemplate(); // -------------------------------> 解決(響應數據可能)中文亂碼 的問題 List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters(); converterList.remove(1); // 移除原來的轉換器 // 設置字符編碼為utf-8 HttpMessageConverter<?> converter = new StringHttpMessageConverter(StandardCharsets.UTF_8); converterList.add(1, converter); // 添加新的轉換器(注:convert順序錯誤會導致失敗) restTemplate.setMessageConverters(converterList); // -------------------------------> (選擇性設置)請求頭信息 // HttpHeaders實現了MultiValueMap接口 HttpHeaders httpHeaders = new HttpHeaders(); // 給請求header中添加一些數據 httpHeaders.add("JustryDeng", "這是一個大帥哥!"); // -------------------------------> 注:GET請求 創建HttpEntity時,請求體傳入null即可 // 請求體的類型任選即可;只要保證 請求體 的類型與HttpEntity類的泛型保持一致即可 String httpBody = null; HttpEntity<String> httpEntity = new HttpEntity<String>(httpBody, httpHeaders); // -------------------------------> URI StringBuffer paramsURL = new StringBuffer("http://127.0.0.1:9527/restTemplate/doHttpGet"); // 字符數據最好encoding一下;這樣一來,某些特殊字符才能傳過去(如:flag的參數值就是“&”,不encoding的話,傳不過去) paramsURL.append("?flag=" + URLEncoder.encode("&", "utf-8")); URI uri = URI.create(paramsURL.toString()); // -------------------------------> 執行請求並返回結果 // 此處的泛型 對應 響應體數據 類型;即:這里指定響應體的數據裝配為String ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET, httpEntity, String.class); // -------------------------------> 響應信息 //響應碼,如:401、302、404、500、200等 System.err.println(response.getStatusCodeValue()); Gson gson = new Gson(); // 響應頭 System.err.println(gson.toJson(response.getHeaders())); // 響應體 if(response.hasBody()) { System.err.println(response.getBody()); } } }
被http請求的對應的方法邏輯為:
注:我們也可以使用@RequestHeader()來獲取到請求頭中的數據信息,如:
結果(效果)展示
1.進行HTTP請求的方法獲得響應后輸出結果為:
2.被HTTP請求的方法被請求后的輸出結果為:
HTTP之POST請求(示例)
/** * RestTemplate 發送 HTTP POST請求 --- 測試 * @throws UnsupportedEncodingException * * @date 2018年9月8日 下午2:12:50 */ @Test public void doHttpPostTest() throws UnsupportedEncodingException { // -------------------------------> 獲取Rest客戶端實例 RestTemplate restTemplate = new RestTemplate(); // -------------------------------> 解決(響應數據可能)中文亂碼 的問題 List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters(); converterList.remove(1); // 移除原來的轉換器 // 設置字符編碼為utf-8 HttpMessageConverter<?> converter = new StringHttpMessageConverter(StandardCharsets.UTF_8); converterList.add(1, converter); // 添加新的轉換器(注:convert順序錯誤會導致失敗) restTemplate.setMessageConverters(converterList); // -------------------------------> (選擇性設置)請求頭信息 // HttpHeaders實現了MultiValueMap接口 HttpHeaders httpHeaders = new HttpHeaders(); // 設置contentType httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8); // 給請求header中添加一些數據 httpHeaders.add("JustryDeng", "這是一個大帥哥!"); // ------------------------------->將請求頭、請求體數據,放入HttpEntity中 // 請求體的類型任選即可;只要保證 請求體 的類型與HttpEntity類的泛型保持一致即可 // 這里手寫了一個json串作為請求體 數據 (實際開發時,可使用fastjson、gson等工具將數據轉化為json串) String httpBody = "{\"motto\":\"唉呀媽呀!腦瓜疼!\"}"; HttpEntity<String> httpEntity = new HttpEntity<String>(httpBody, httpHeaders); // -------------------------------> URI StringBuffer paramsURL = new StringBuffer("http://127.0.0.1:9527/restTemplate/doHttpPost"); // 字符數據最好encoding一下;這樣一來,某些特殊字符才能傳過去(如:flag的參數值就是“&”,不encoding的話,傳不過去) paramsURL.append("?flag=" + URLEncoder.encode("&", "utf-8")); URI uri = URI.create(paramsURL.toString()); // -------------------------------> 執行請求並返回結果 // 此處的泛型 對應 響應體數據 類型;即:這里指定響應體的數據裝配為String ResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.POST, httpEntity, String.class); // -------------------------------> 響應信息 //響應碼,如:401、302、404、500、200等 System.err.println(response.getStatusCodeValue()); Gson gson = new Gson(); // 響應頭 System.err.println(gson.toJson(response.getHeaders())); // 響應體 if(response.hasBody()) { System.err.println(response.getBody()); } }
被http請求的對應的方法邏輯為:
注:我們也可以使用@RequestHeader()來獲取到請求頭中的數據信息,如:
結果(效果)展示
進行HTTP請求的方法獲得響應后輸出結果為:
被HTTP請求的方法被請求后的輸出結果為:
HTTPS請求的准備工作
HTTPS請求 = 超文本傳輸協議HTTP + 安全套接字層SSL。
先給出等下需要用到的一個SimpleClientHttpRequestFactory的實現類
/** * 聲明:此代碼摘錄自https://blog.csdn.net/wltsysterm/article/details/80977455 * 聲明:關於Socket的相關知識,本人會在后面的閑暇時間進行學習整理,請持續關注博客更新 * * @author JustryDeng * @DATE 2018年9月8日 下午4:34:02 */ public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory { @Override protected void prepareConnection(HttpURLConnection connection, String httpMethod) { try { if (!(connection instanceof HttpsURLConnection)) { throw new RuntimeException("An instance of HttpsURLConnection is expected"); } HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) { } @Override public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory())); httpsConnection.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true; } }); super.prepareConnection(httpsConnection, httpMethod); } catch (Exception e) { e.printStackTrace(); } } /** * We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"}); * see http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section) */ // SSLSocketFactory用於創建 SSLSockets private static class MyCustomSSLSocketFactory extends SSLSocketFactory { private final SSLSocketFactory delegate; public MyCustomSSLSocketFactory(SSLSocketFactory delegate) { this.delegate = delegate; } // 返回默認啟用的密碼套件。除非一個列表啟用,對SSL連接的握手會使用這些密碼套件。 // 這些默認的服務的最低質量要求保密保護和服務器身份驗證 @Override public String[] getDefaultCipherSuites() { return delegate.getDefaultCipherSuites(); } // 返回的密碼套件可用於SSL連接啟用的名字 @Override public String[] getSupportedCipherSuites() { return delegate.getSupportedCipherSuites(); } @Override public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose) throws IOException { final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final String host, final int port) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final InetAddress host, final int port) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, final int localPort) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); return overrideProtocol(underlyingSocket); } private Socket overrideProtocol(final Socket socket) { if (!(socket instanceof SSLSocket)) { throw new RuntimeException("An instance of SSLSocket is expected"); } ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1"}); return socket; } } }
HTTPS之GET請求
說明:RestTemplate發送HTTPS與發送HTTP的代碼,除了在創建RestTemplate時不一樣以及協議不一樣
(一個URL是http開頭,一個是https開頭)外,其余的都一樣。
HTTP獲取RestTemplate實例
HTTPS獲取RestTemplate實例
給出具體HTTPS發送GET請求代碼示例(HTTPS發送POST請求類比即可)
注:HTTPS與HTTP的使用不同之處,在途中已經圈出了。
注:上圖中請求的https://tcc.taobao.com/cc/json/mobile_tel_segment.htm是阿里提供的一個簡單查詢手機信息的地址。
運行該主函數,控制台打印出的結果為:
注:如果用HTTP協議開頭的URL去訪問HTTPS開頭的URL的話(這兩個URL除了協議不同其它都相同),是訪問不了的;除非服
務端有相應的設置。
注:發送HTTPS的邏輯代碼是可以拿來發送HTTP的。但是根據我們寫得HttpsClientRequestFactory類中的代碼可知,會打
印出異常(異常拋出后被catch了):
如果用HTTPS訪問HTTP時不想拋出異常,那么把對應的這個邏輯去掉即可。
提示:“發送HTTPS的邏輯代碼是可以拿來發送HTTP的”這句話的意思是:拿來做發HTTPS請求的邏輯,可以復用來作發HTTP請
求的邏輯。並不是說說一個API能被HTTPS協議的URL訪問,就一定能被HTTP協議的URL訪問。
HTTPS之GET請求
注:關於HTTPS這里只給出了一個GET示例,使用HTTPS進行POST請求也是與HTTP進行POST請求也只是創建
RestTemplate實例和協議不一樣,其余的都一樣;類比GET即可,這里就不再給出示例了。
參考鏈接、摘錄內容出處
https://www.cnblogs.com/duanxz/p/3510622.html
https://blog.csdn.net/wltsysterm/article/details/80977455
https://blog.csdn.net/zhoumengshun/article/details/79100053
如有不當之處,歡迎指正
本次示例測試代碼項目托管鏈接
https://github.com/JustryDeng/PublicRepository
本文已經被收錄進《程序員成長筆記(三)》,筆者JustryDeng
---------------------
作者:justry_deng
來源:CSDN
原文:https://blog.csdn.net/justry_deng/article/details/82531306
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
spring boot resttemplate 使用及支持https協議
RestTemplate 使用
添加httpclient依賴
<!-- httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency>
配置類
package net.fanci.stars.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.client.RestTemplate; import java.util.Arrays; import java.util.List; /** * @author xxx * @create 2018/06/28 15:32 * @description RestTemplate配置類: * 1.將 HttpClient 作為 RestTemplate 的實現,添加 httpclient 依賴即可 * 2.設置響應類型和內容類型 */ @Configuration public class RestConfiguration { @Autowired private RestTemplateBuilder builder; @Bean public RestTemplate restTemplate() { return builder .additionalMessageConverters(new WxMappingJackson2HttpMessageConverter()) .build(); } class WxMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter { WxMappingJackson2HttpMessageConverter() { List<MediaType> mediaTypes = Arrays.asList( MediaType.TEXT_PLAIN, MediaType.TEXT_HTML, MediaType.APPLICATION_JSON_UTF8 ); setSupportedMediaTypes(mediaTypes);// tag6 } } }
使用方法(如:通過微信code獲取token信息)
@Autowired private RestTemplate restTemplate; /** * 獲取access_token的完整信息 * * @param code * @return */ @Override public WechatAuthAccesstoken getWechatAuthAccesstoken(String code) { String url = ACCESS_TOKEN_URL + "appid=" + wechatData.getAppID() + "&secret=" + wechatData.getAppsecret() + "&code=" + code + "&grant_type=authorization_code"; // com.alibaba.fastjson JSONObject jsonObject = restTemplate.getForObject(url, JSONObject.class); WechatAuthAccesstoken wechatAuthAccesstoken = new WechatAuthAccesstoken(); if (jsonObject != null) { wechatAuthAccesstoken.setId(PayUtil.genUniqueKey()); wechatAuthAccesstoken.setCreatedDate(DateTime.now().toDate()); wechatAuthAccesstoken.setModifiedDate(DateTime.now().toDate()); wechatAuthAccesstoken.setAccessToken((String) jsonObject.get("access_token")); DateTime now = DateTime.now(); DateTime expired = now.plusSeconds((Integer) jsonObject.get("expires_in")); wechatAuthAccesstoken.setExpires(expired.toDate()); wechatAuthAccesstoken.setRefreshToken(jsonObject.getString("refresh_token")); wechatAuthAccesstoken.setOpenid((String) jsonObject.get("openid")); wechatAuthAccesstoken.setScope(jsonObject.getString("scope")); int isOk = tokenMapper.insert(wechatAuthAccesstoken); if (isOk > 0) { logger.info("本地存儲access_token信息成功"); return wechatAuthAccesstoken; } else { logger.error("本地存儲access_token信息失敗"); } } else { logger.error("獲取access_token信息失敗"); } return null; }
https支持
配置類
import org.springframework.http.client.SimpleClientHttpRequestFactory; import javax.net.ssl.*; import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.Socket; import java.security.cert.X509Certificate; /** * @author xxx * @create 2018/07/16 11:41 * @description 創建 HttpsClientRequestFactory 以支持 RestTemplate 調用 https 請求 */ public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory { @Override protected void prepareConnection(HttpURLConnection connection, String httpMethod) { try { if (!(connection instanceof HttpsURLConnection)) { throw new RuntimeException("An instance of HttpsURLConnection is expected"); } HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; TrustManager[] trustAllCerts = 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) { } } }; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory())); httpsConnection.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String s, SSLSession sslSession) { return true; } }); super.prepareConnection(httpsConnection, httpMethod); } catch (Exception e) { e.printStackTrace(); } } /** * We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"}); * see http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section) */ private static class MyCustomSSLSocketFactory extends SSLSocketFactory { private final SSLSocketFactory delegate; public MyCustomSSLSocketFactory(SSLSocketFactory delegate) { this.delegate = delegate; } @Override public String[] getDefaultCipherSuites() { return delegate.getDefaultCipherSuites(); } @Override public String[] getSupportedCipherSuites() { return delegate.getSupportedCipherSuites(); } @Override public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose) throws IOException { final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final String host, final int port) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final InetAddress host, final int port) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port); return overrideProtocol(underlyingSocket); } @Override public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, final int localPort) throws IOException { final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort); return overrideProtocol(underlyingSocket); } private Socket overrideProtocol(final Socket socket) { if (!(socket instanceof SSLSocket)) { throw new RuntimeException("An instance of SSLSocket is expected"); } ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1"}); return socket; } } }
使用類
String message = null; String url = "https://ip:port/xxx"; RestTemplate restTemplateHttps = new RestTemplate(new HttpsClientRequestFactory()); List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("UTF-8")); messageConverters.add(stringHttpMessageConverter); restTemplateHttps.setMessageConverters(messageConverters); ResponseEntity<String> responseEntity = restTemplateHttps.postForEntity(url, paramsData, String.class); if (responseEntity != null && responseEntity.getStatusCodeValue() == 200) { message = responseEntity.getBody(); }
離殤一曲與誰眠: 要依賴有什么用全是JDK和springboot自帶的包
---------------------
作者:uanei
來源:CSDN
原文:https://blog.csdn.net/u013469944/article/details/84193792
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
1. 為什么使用HttpClient?
一開始其實是考慮使用RestTemplate的,但遇到的難題自然是SSL認證以及NTLM的認證.以目前的RestTemplate還做不到NTLM認證.而且使用SSL認證的過程也是挺復雜的. 復雜的是:居然還是要借助HttpClient .
@Bean public RestTemplate buildRestTemplate(List<CustomHttpRequestInterceptor> interceptors) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException { HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); factory.setConnectionRequestTimeout(requestTimeout); factory.setConnectTimeout(connectTimeout); factory.setReadTimeout(readTimeout); // https SSLContextBuilder builder = new SSLContextBuilder(); builder.loadTrustMaterial(null, (X509Certificate[] x509Certificates, String s) -> true); SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(builder.build(), new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE); Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", new PlainConnectionSocketFactory()) .register("https", socketFactory).build(); PoolingHttpClientConnectionManager phccm = new PoolingHttpClientConnectionManager(registry); phccm.setMaxTotal(200); CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).setConnectionManager(phccm).setConnectionManagerShared(true).build(); factory.setHttpClient(httpClient); RestTemplate restTemplate = new RestTemplate(factory); List<ClientHttpRequestInterceptor> clientInterceptorList = new ArrayList<>(); for (CustomHttpRequestInterceptor i : interceptors) { ClientHttpRequestInterceptor interceptor = i; clientInterceptorList.add(