Android筆記-網路篇:HTTP & HTTPS

Chopper_zjxstar發表於2018-01-29

HTTP

基礎

超文字傳輸協議(HTTP,HyperText Transfer Protocol),預設埠:80

特點

  1. 無連線:無連線的含義是限制每次連線只處理一個請求。伺服器處理完客戶的請求,並收到客戶的應答後,即斷開連線。採用這種方式可以節省傳輸時間。
  2. 無狀態:HTTP協議是無狀態協議,無狀態是指協議對於事務處理沒有記憶能力。缺少狀態意味著如果後續處理需要前面的資訊,則它必須重傳,這樣可能導致每次連線傳送的資料量增大。另一方面,在伺服器不需要先前資訊時它的應答就較快。

請求報文

Http請求頭
  一般由請求行、請求頭、空行、請求體四部分組成。

響應報文

Http響應頭
  一般由狀態行、訊息報頭、空行、響應正文四部分組成。

常見狀態碼

  • 200 OK:客戶端請求成功
  • 302 Move temporarily:請求的資源臨時從不同的 URI響應請求,重定向
  • 304 Not Modified:有效快取
  • 400 Bad Request:客戶端請求有語法錯誤,不能被伺服器所理解
  • 403 Forbidden:伺服器收到請求,但是拒絕提供服務
  • 404 Not Found:請求失敗,請求所希望得到的資源未被在伺服器上發現
  • 500 Internal Server Error:伺服器發生不可預期的錯誤
  • 503 Server Unavailable:伺服器當前不能處理客戶端的請求,一段時間後可能恢復正常

常見通用報頭

  • Cache-Control:指定請求和響應遵循的快取機制

常用請求報頭

  • Host:請求的主機名,允許多個域名同處一個IP地址,即虛擬主機
  • User-Agent:傳送請求的瀏覽器型別、作業系統等資訊
  • Accept:客戶端可識別的內容型別列表,用於指定客戶端接收那些型別的資訊
  • Accept-Encoding:客戶端可識別的資料編碼
  • Connection:允許客戶端和伺服器指定與請求/響應連線有關的選項,例如這是為Keep-Alive則表示保持連線
  • Referer:允許客戶端指定請求uri的源資源地址,這可以允許伺服器生成回退連結串列,可用來登陸、優化cache等
  • Range:可以請求實體的一個或者多個子範圍

常見響應報頭

  • Location:用於重定向接受者到一個新的位置,常用在更換域名的時候
  • Server:包含可伺服器用來處理請求的系統資訊,與User-Agent請求報頭是相對應的

常見實體報頭

  • Content-Type:傳送給接收者的實體正文的媒體型別
  • Content-Lenght:實體正文的長度
  • Content-Encoding:實體報頭被用作媒體型別的修飾符,它的值指示了已經被應用到實體正文的附加內容的編碼,因而要獲得Content-Type報頭域中所引用的媒體型別,必須採用相應的解碼機制
  • Last-Modified:實體報頭用於指示資源的最後修改日期和時間
  • Expires:實體報頭給出響應過期的日期和時間

SPDY

2012年google提出的SPDY方案,主要解決:

  1. 降低延遲:SPDY採取了多路複用(multiplexing)。多路複用通過多個請求stream共享一個tcp連線的方式;
  2. 請求優先順序:SPDY允許給每個request設定優先順序,這樣重要的請求就會優先得到響應;
  3. header壓縮:選擇合適的壓縮演算法可以減小包的大小和數量;
  4. 基於HTTPS的加密協議傳輸
  5. 服務端推送

SPDY

Http2.0

  可以看成是SPDY的升級版。

Http2.0與SPDY的區別

  • HTTP2.0 支援明文 HTTP 傳輸,而 SPDY 強制使用 HTTPS
  • HTTP2.0 訊息頭的壓縮演算法採用 HPACK,而非 SPDY 採用的 DEFLATE

Http2.0新特性

  • 新的二進位制格式:HTTP1.x的解析是基於文字的
  • 多路複用:即連線共享,即每一個request都是是用作連線共享機制的
  • header壓縮
  • 服務端推送

HTTPS

(Hyper Text Transfer Protocol over Secure Socket Layer),是以安全為目標的HTTP通道,即HTTP下加入SSL層,埠:443

HTTPS握手流程

Https流程
注意:下面流程與圖中序列不是一致的。

  1. 客戶端的瀏覽器向伺服器傳送客戶端SSL協議的版本號,加密演算法的種類,產生的隨機數,以及其他伺服器和客戶端之間通訊所需要的各種資訊;
  2. 伺服器向客戶端傳送SSL協議的版本號,加密演算法的種類,隨機數以及其他相關資訊,同時伺服器還將向客戶端傳送自己的證照;
  3. 客戶利用伺服器傳過來的資訊驗證伺服器的合法性,伺服器的合法性包括:證照是否過期,發行伺服器證照的CA是否可靠,發行者證照的公鑰能否正確解開伺服器證照的“發行者的數字簽名”,伺服器證照上的域名是否和伺服器的實際域名相匹配。如果合法性驗證沒有通過,通訊將斷開;如果合法性驗證通過,將繼續進行第四步;
  4. 使用者端隨機產生一個用於後面通訊的“對稱密碼”,然後用伺服器的公鑰(伺服器的公鑰從步驟②中的伺服器的證照中獲得)對其加密,然後將加密後的“預主密碼”傳給伺服器;
  5. 如果伺服器要求客戶的身份認證(在握手過程中為可選),使用者可以建立一個隨機數然後對其進行資料簽名,將這個含有簽名的隨機數和客戶自己的證照以及加密過的“預主密碼”一起傳給伺服器;
  6. 如果伺服器要求客戶的身份認證,伺服器必須檢驗客戶證照和簽名隨機數的合法性,具體的合法性驗證過程包括:客戶的證照使用日期是否有效,為客戶提供證照的CA是否可靠,發行CA 的公鑰能否正確解開客戶證照的發行CA的數字簽名,檢查客戶的證照是否在證照廢止列表(CRL)中。檢驗如果沒有通過,通訊立刻中斷;如果驗證通過,伺服器將用自己的私鑰解開加密的“預主密碼”,然後執行一系列步驟來產生主通訊密碼(客戶端也將通過同樣的方法產生相同的主通訊密碼);
  7. 伺服器和客戶端用相同的主密碼即“通話密碼”,一個對稱金鑰用於SSL協議的安全資料通訊的加解密通訊。同時在SSL通訊過程中還要完成資料通訊的完整性,防止資料通訊中的任何變化;
  8. 客戶端向伺服器端發出資訊,指明後面的資料通訊將使用的步驟⑦中的主密碼為對稱金鑰,同時通知伺服器客戶端的握手過程結束;
  9. 伺服器向客戶端發出資訊,指明後面的資料通訊將使用的步驟⑦中的主密碼為對稱金鑰,同時通知客戶端伺服器端的握手過程結束;
  10. SSL 的握手部分結束,SSL安全通道的資料通訊開始,客戶和伺服器開始使用相同的對稱金鑰進行資料通訊,同時進行通訊完整性的檢驗。

Android中處理Https

官方推薦:Android推薦方案

處理 javax.net.ssl.SSLHandshakeException: 證照驗證失敗

/**
     * HTTPS未知的證照頒發機構處理方法
     * Android客戶端儲存證照
     *
     * @param input 待信任的CA證照流
     * @return SSLContext
     */
    public static SSLContext getSSLContext(InputStream input) {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            Certificate ca = cf.generateCertificate(input);

            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null, null);
            keyStore.setCertificateEntry("ca", ca);

            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(keyStore);

            sslContext.init(null, tmf.getTrustManagers(), null);
            input.close();
            return sslContext;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }

        return null;
    }
複製程式碼

單獨使用SSL和HTTP

/**
     * 單獨使用SSL + HTTP時
     * @param trustManagers
     * @return
     */
    public static SSLContext getSSLContext(TrustManager[] trustManagers) {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustManagers, null);
            return sslContext;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static TrustManager[] sDefaultTrustManagers = new TrustManager[] {new X509TrustManager() {

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            // TODO: 2018/1/23雙向校驗中,向服務端發客戶端證照
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            // TODO: 2018/1/23 雙向校驗中,校驗服務端證照
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }};

    public static HostnameVerifier sHostnameVerifier = new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            // 不驗證主機名
            return true;
        }
    };
複製程式碼

相關文章