RestTemplate發送HTTP、HTTPS請求

 

RestTemplate 使用總結

 

場景:

認證服務器須要有個 http client 把前端發來的請求轉發到 backend service, 而後把 backend service 的結果再返回給前端,服務器自己只作認證功能。html

遇到的問題:

  • 長鏈接以保證高性能。RestTemplate 自己也是一個 wrapper 其底層默認是 SimpleClientHttpRequestFactory ,若是要保證長鏈接, HttpComponentsClientHttpRequestFactory 是個更好的選擇,它不只能夠控制可以創建的鏈接數還能細粒度的控制到某個 server 的鏈接數,很是方便。在默認狀況下,RestTemplate 到某個 server 的最大鏈接數只有 2, 通常須要調的更高些,最好等於 server 的 CPU 個數前端

  • access_token 不該傳到 backend service. backend service 之間通訊不須要 token,由於到這些服務的請求都是已經認證過的,是可信賴的用戶發出的請求。所以轉發請求時要把 parameter 從 request url 中刪掉。刪除 parameter 說難不難,說簡單其實還有點麻煩,網上有一個 UrlEncodedQueryString 能夠參考下,它封裝了不少函數,其中就包括從url 中摘掉指定 headerjava

  • 請求的 HttpMethod 問題。 HttpMethod 有不少種,http client 不該該對每種 Http method 都單獨處理,因此應選用 RestTemplate 的 exchange 方法。exchange 方法要求給出 RequestBody 參數,而對於 Get 請求,這部分每每爲空,因此咱們要在 controller 中聲明 @RequestBody(required = false) String bodygit

  • 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


    github

Spring Boot忽略https證書:No subject alternative names present

springboot--resttemplate訪問https請求


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能夠配置鏈接池和證書等信息。

 

軟硬件環境: Windows十、Eclipse、JDK1.八、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);

// -------------------------------> 響應信息
//響應碼,如:40一、30二、40四、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);

// -------------------------------> 響應信息
//響應碼,如:40一、30二、40四、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(interceptor); } restTemplate.setInterceptors(clientInterceptorList); return restTemplate; } 複製代碼

2. 爲何要繞過SSL認證?

至於爲何要繞過SSL認證,由於裝證書的這些操做我並不會.同時也想試試能不能忽略這個證書認證調用接口.

  • 首先若是想繞過證書,都必先建立X509TrustManager這個對象而且重寫它的方法.

X509TrustManager該接口是一個用於Https的證書信任管理器,咱們能夠在這裏添加咱們的證書,讓該管理器知道咱們有那些證書是能夠信任的.

該接口會有三個方法:

void checkClientTrusted(X509Certificate[] xcs, String str) void checkServerTrusted(X509Certificate[] xcs, String str) X509Certificate[] getAcceptedIssuers() 複製代碼
  1. 第一個方法checkClientTrusted.該方法檢查客戶端的證書,若不信任該證書則拋出異常。因爲咱們不須要對客戶端進行認證,所以咱們只須要執行默認的信任管理器的這個方法。JSSE中,默認的信任管理器類爲TrustManager。

  2. 第二個方法checkServerTrusted.該方法檢查 服務器 的證書,若不信任該證書一樣拋出異常。經過本身實現該方法,可使之信任咱們指定的任何證書。在實現該方法時,也能夠簡單的不作任何處理,即一個空的函數體,因爲不會拋出異常,它就會信任任何證書。

  3. 第三個方法getAcceptedIssusers,返回受信任的X509證書數組。

而咱們只須要重寫這三個方法,而且不須要修改裏面的內容.而後再交給HttpClient就能夠實現繞過SSL認證了.

X509TrustManager trustManager = new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] xcs, String str) { } @Override public void checkServerTrusted(X509Certificate[] xcs, String str) { } SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.SSL); ctx.init(null, new TrustManager[]{trustManager}, null); //生成工廠 SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE); //並註冊到HttpClient中 Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", socketFactory).build(); HttpClientBuilder httpClientBuilder = HttpClients.custom().setConnectionManager(connectionManager); CloseableHttpClient httpClient = httpClientBuilder.build(); 複製代碼

回顧一下步驟:

  1. 建立X509TrustManager對象並重寫方法.
  2. 建立SSLContext實例,並交到工廠管理.
  3. 註冊到HttpClient中.經過ConnectionManager最後生成httpClient.

3. 什麼是NTLM?

NTLM是NT LAN Manager的縮寫,這也說明了協議的來源。NTLM 是 Windows NT 早期版本的標準安全協議,Windows 2000 支持 NTLM 是爲了保持向後兼容。Windows 2000內置三種基本安全協議之一。

NTLM的原理

NTLM的工做原理描述

其實我對這個瞭解得不是很深,由於趕上這種狀況的感受不會不少,因此網上的資源也不太多. 這裏只是針對HttpClient趕上NTLM認證的狀況詳細描述一下.有興趣的朋友能夠經過以上的連接瞭解下.

4. 如何使用HttpClient進行NTLM認證?

這個查閱了官網的文檔.官網也給出瞭解決方案.

hc.apache.org/httpcompone…

須要把這幾個類編寫一下.

JCIFSEngine:

public final class JCIFSEngine implements NTLMEngine { private static final int TYPE_1_FLAGS = NtlmFlags.NTLMSSP_NEGOTIATE_56 | NtlmFlags.NTLMSSP_NEGOTIATE_128 | NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 | NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN | NtlmFlags.NTLMSSP_REQUEST_TARGET; @Override public String generateType1Msg(final String domain, final String workstation) throws NTLMEngineException { final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation); return Base64.encode(type1Message.toByteArray()); } @Override public String generateType3Msg(final String username, final String password, final String domain, final String workstation, final String challenge) throws NTLMEngineException { Type2Message type2Message; try { type2Message = new Type2Message(Base64.decode(challenge)); } catch (final IOException exception) { throw new NTLMEngineException("Invalid NTLM type 2 message", exception); } final int type2Flags = type2Message.getFlags(); final int type3Flags = type2Flags & (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER)); final Type3Message type3Message = new Type3Message(type2Message, password, domain, username, workstation, type3Flags); return Base64.encode(type3Message.toByteArray()); } } 複製代碼

JCIFSNTLMSchemeFactory:

public class JCIFSNTLMSchemeFactory implements AuthSchemeProvider { public AuthScheme create(final HttpContext context){ return new NTLMScheme(new JCIFSEngine()); } } 複製代碼

最後就在HttpClient註冊:

Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create() .register(AuthSchemes.NTLM, new JCIFSNTLMSchemeFactory()) .register(AuthSchemes.BASIC, new BasicSchemeFactory()) .register(AuthSchemes.DIGEST, new DigestSchemeFactory()) .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory()) .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory()) .build(); CloseableHttpClient httpClient = HttpClients.custom() .setDefaultAuthSchemeRegistry(authSchemeRegistry) .build(); 複製代碼

最後就同時使用繞過SSL驗證以及NTLM驗證:

private static PoolingHttpClientConnectionManager connectionManager; private static RequestConfig requestConfig; private static Registry<AuthSchemeProvider> authSchemeRegistry; private static Registry<ConnectionSocketFactory> socketFactoryRegistry; private static CredentialsProvider credsProvider; public void init() { try { X509TrustManager trustManager = new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] xcs, String str) { } @Override public void checkServerTrusted(X509Certificate[] xcs, String str) { } }; SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.SSL); ctx.init(null, new TrustManager[]{trustManager}, null); SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE); NTCredentials creds = new NTCredentials("用戶名", "密碼", "工做站(workstation)", "域名"); credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials(AuthScope.ANY, creds); socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", socketFactory).build(); connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); connectionManager.setMaxTotal(18); connectionManager.setDefaultMaxPerRoute(6); requestConfig = RequestConfig.custom() .setSocketTimeout(30000) .setConnectTimeout(30000) .build(); authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create() .register(AuthSchemes.NTLM, new JCIFSNTLMSchemeFactory()) .register(AuthSchemes.BASIC, new BasicSchemeFactory()) .register(AuthSchemes.DIGEST, new DigestSchemeFactory()) .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory()) .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory()) .build(); } catch (Exception e) { e.printStackTrace(); } } 複製代碼

 

以上就是本文的所有內容,但願本文的內容對你們的學習或者工做能帶來必定的幫助,也但願你們多多支持 碼農網

爲你推薦:

https://www.codercto.com/a/36829.html

相關文章
相關標籤/搜索