易筋SpringBoot 2.1 | 第五篇:RestTemplate請求https(3)
寫作時間:2018-12-28
Spring Boot: 2.1 ,JDK: 1.8, IDE: IntelliJ IDEA,
說明
截至2018年6月,Alexa排名前100萬的網站中有34.6%使用HTTPS作為預設值[1],網際網路141387個最受歡迎網站的43.1%具有安全實施的HTTPS[2],以及45%的頁面載入(透過Firefox紀錄)使用HTTPS[3]。2017年3月,中國註冊域名總數的0.11%使用HTTPS。[4]
超文字傳輸安全協議(英語:Hypertext Transfer Protocol Secure,縮寫:HTTPS,常稱為HTTP over TLS,HTTP over SSL或HTTP Secure)是一種透過計算機網路進行安全通訊的傳輸協議。HTTPS經由HTTP進行通訊,但利用SSL/TLS來加密資料包。HTTPS開發的主要目的,是提供對網站伺服器的身份認證,保護交換資料的隱私與完整性。這個協議由網景公司(Netscape)在1994年首次提出,隨後擴充套件到網際網路上。
以上內容來自維基百科。
本文主要說明用RestTemplate請求HTTPS。搜尋引擎搜尋的時候請求用的是Spring Boot https Client; 預設搜尋結果Spring Boot https Server。
獲取天氣預報api
為了測試HTTPS,先找一個免費的天氣預報資源:
- 開啟和風網站,網址是這個https://www.heweather.com,然後註冊賬號,找到自己的KEY,再開啟這個API說明。https://www.heweather.com/documents/api/s6/weather-forecast
得出測試連結格式為:
https://free-api.heweather.net/s6/weather/forecast?location= $&key=$
- 在網址http://www.weather.com.cn/weather/101280101.shtml找到城市的天氣預報,這裡以廣州為例,最後得出的測試連結為
https://free-api.heweather.net/s6/weather/forecast?location=CN101280101&key=34a265026b544eac9b3dcd9f104a7bb6
網頁請求結果
{"HeWeather6":
[{"basic": {"cid":"CN101280101","location":"廣州","parent_city":"廣州","admin_area" :"廣東","cnty":"中國","lat":"23.12517738","lon":"113.28063965","tz":"+8.00"},
"update":{"loc":"2018-12-28 20:57","utc":"2018-12-28 12:57"},
"status":"ok",
"daily_forecast":[{"cond_code_d":"101","cond_code_n":"104","cond_txt_d":"多雲","cond_txt_n":"陰","date":"2018-12-28","hum":"58","mr":"23:47","ms":"11:44","pcpn":"5.0","pop":"80","pres":"1024","sr":"07:07","ss":"17:50","tmp_max":"17","tmp_min":"7","uv_index":"0","vis":"10","wind_deg":"359","wind_dir":"北風","wind_sc":"4-5","wind_spd":"28"},{"cond_code_d":"104","cond_code_n":"104","cond_txt_d":"陰","cond_txt_n":"陰","date":"2018-12-29","hum":"53","mr":"00:00","ms":"12:25","pcpn":"1.0","pop":"56","pres":"1028","sr":"07:07","ss":"17:51","tmp_max":"12","tmp_min":"5","uv_index":"1","vis":"20","wind_deg":"6","wind_dir":"北風","wind_sc":"4-5","wind_spd":"27"},{"cond_code_d":"104","cond_code_n":"305","cond_txt_d":"陰","cond_txt_n":"小雨","date":"2018-12-30","hum":"41","mr":"00:45","ms":"13:04","pcpn":"5.0","pop":"80","pres":"1029","sr":"07:07","ss":"17:52","tmp_max":"11","tmp_min":"5","uv_index":"1","vis":"20","wind_deg":"0","wind_dir":"北風","wind_sc":"4-5","wind_spd":"26"}]
}]
}
工程建立
參照教程【SpringBoot 2.1 | 第一篇:構建第一個SpringBoot工程】新建一個Spring Boot專案,名字叫demoresttemplatehttps, 在目錄src/main/java/resources
下找到配置檔案application.properties
,重新命名為application.yml
。
pom引入httpclient依賴
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
新建RestTemplate配置類
配置可以通過註解 @Configuration ,全域性獲取。根據最小適用原則,建立配置類com.zgpeace.demoresttemplatehttps.config.RestTemplateConfig
實現程式碼有註釋掉的部分,暫時用不到,後面會說明。
package com.zgpeace.demoresttemplatehttps.config;
//import org.apache.http.auth.AuthScope;
//import org.apache.http.auth.UsernamePasswordCredentials;
//import org.apache.http.client.CredentialsProvider;
//import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
//import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
//import org.apache.http.impl.client.BasicCredentialsProvider;
//import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
// unable to find valid certification path to requested target
/*
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE);
*/
// 401 Unauthorized
/*
String host = "localhttps";
int port = 23333;
String user = "john";
String password = "123456";
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(host, port),
new UsernamePasswordCredentials(user, password));
*/
CloseableHttpClient httpClient
= HttpClients.custom()
.setSSLHostnameVerifier(new NoopHostnameVerifier())
// .setSSLSocketFactory(sslConnectionSocketFactory)
// .setDefaultCredentialsProvider(credsProvider)
.build();
HttpComponentsClientHttpRequestFactory requestFactory
= new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}
}
呼叫介面
完善類 com.zgpeace.demoresttemplatehttps.DemoresttemplatehttpsApplication
package com.zgpeace.demoresttemplatehttps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
public class DemoresttemplatehttpsApplication {
@Autowired
RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(DemoresttemplatehttpsApplication.class, args);
}
@Bean
public CommandLineRunner run() throws Exception {
return args -> {
String url = "https://free-api.heweather.net/s6/weather/forecast?location=CN101280101&key=34a265026b544eac9b3dcd9f104a7bb6";
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
String body = responseEntity.getBody();
System.out.println("body --> " + body);
};
}
}
通過 @Autowire 注入restTemplate物件。
控制檯列印日誌:
body --> {"HeWeather6":
[{"basic":{"cid":"CN101280101","location":"廣州","parent_city":"廣州","admin_area":"廣東","cnty":"中國","lat":"23.12517738","lon":"113.28063965","tz":"+8.00"},
"update":{"loc":"2018-12-28 21:57","utc":"2018-12-28 13:57"},
"status":"ok",
"daily_forecast":[{"cond_code_d":"101","cond_code_n":"104","cond_txt_d":"多雲","cond_txt_n":"陰","date":"2018-12-28","hum":"58","mr":"23:47","ms":"11:44","pcpn":"5.0","pop":"80","pres":"1024","sr":"07:07","ss":"17:50","tmp_max":"17","tmp_min":"7","uv_index":"0","vis":"10","wind_deg":"9","wind_dir":"北風","wind_sc":"4-5","wind_spd":"31"},{"cond_code_d":"104","cond_code_n":"104","cond_txt_d":"陰","cond_txt_n":"陰","date":"2018-12-29","hum":"53","mr":"00:00","ms":"12:25","pcpn":"1.0","pop":"56","pres":"1028","sr":"07:07","ss":"17:51","tmp_max":"12","tmp_min":"5","uv_index":"1","vis":"20","wind_deg":"359","wind_dir":"北風","wind_sc":"4-5","wind_spd":"30"},{"cond_code_d":"104","cond_code_n":"305","cond_txt_d":"陰","cond_txt_n":"小雨","date":"2018-12-30","hum":"41","mr":"00:45","ms":"13:04","pcpn":"5.0","pop":"80","pres":"1029","sr":"07:07","ss":"17:52","tmp_max":"11","tmp_min":"5","uv_index":"1","vis":"20","wind_deg":"9","wind_dir":"北風","wind_sc":"4-5","wind_spd":"26"}]
}]
}
證書無效的網站
證書無效的網站,請求會報錯unable to find valid certification path to requested target
,解決方案如下:
// unable to find valid certification path to requested target
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(builder.build(), NoopHostnameVerifier.INSTANCE);
CloseableHttpClient httpClient
= HttpClients.custom()
.setSSLHostnameVerifier(new NoopHostnameVerifier())
.setSSLSocketFactory(sslConnectionSocketFactory)
// .setDefaultCredentialsProvider(credsProvider)
.build();
需要授權登入的網站
需要授權登入的網站,需要使用者名稱密碼,如下圖演示:
解決方案如下:
// 401 Unauthorized
String host = "localhttps";
int port = 23333;
String user = "john";
String password = "123456";
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(host, port),
new UsernamePasswordCredentials(user, password));
CloseableHttpClient httpClient
= HttpClients.custom()
.setSSLHostnameVerifier(new NoopHostnameVerifier())
// .setSSLSocketFactory(sslConnectionSocketFactory)
.setDefaultCredentialsProvider(credsProvider)
.build();
總結
恭喜你!學會了RestTemplate請求https。
程式碼下載:https://github.com/zgpeace/Spring-Boot2.1/tree/master/demoresttemplatehttps
參考:
http://www.rain1024.com/2017/04/26/api-article76/
http://www.tutorialsteacher.com/https/what-is-https
https://developers.google.com/web/fundamentals/security/encrypt-in-transit/why-https
https://zh.wikipedia.org/wiki/超文字傳輸安全協議
https://o7planning.org/en/11649/secure-spring-boot-restful-service-using-basic-authentication