1. 程式人生 > >易筋SpringBoot 2.1 | 第五篇:RestTemplate請求https(3)

易筋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,先找一個免費的天氣預報資源:

  1. 開啟和風網站,網址是這個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=$
  1. 在網址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