HTTP
基礎
超文字傳輸協議(HTTP,HyperText Transfer Protocol),預設埠:80
特點
- 無連線:無連線的含義是限制每次連線只處理一個請求。伺服器處理完客戶的請求,並收到客戶的應答後,即斷開連線。採用這種方式可以節省傳輸時間。
- 無狀態: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方案,主要解決:
- 降低延遲:SPDY採取了多路複用(multiplexing)。多路複用通過多個請求stream共享一個tcp連線的方式;
- 請求優先順序:SPDY允許給每個request設定優先順序,這樣重要的請求就會優先得到響應;
- header壓縮:選擇合適的壓縮演算法可以減小包的大小和數量;
- 基於HTTPS的加密協議傳輸
- 服務端推送
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握手流程
注意:下面流程與圖中序列不是一致的。- 客戶端的瀏覽器向伺服器傳送客戶端SSL協議的版本號,加密演算法的種類,產生的隨機數,以及其他伺服器和客戶端之間通訊所需要的各種資訊;
- 伺服器向客戶端傳送SSL協議的版本號,加密演算法的種類,隨機數以及其他相關資訊,同時伺服器還將向客戶端傳送自己的證照;
- 客戶利用伺服器傳過來的資訊驗證伺服器的合法性,伺服器的合法性包括:證照是否過期,發行伺服器證照的CA是否可靠,發行者證照的公鑰能否正確解開伺服器證照的“發行者的數字簽名”,伺服器證照上的域名是否和伺服器的實際域名相匹配。如果合法性驗證沒有通過,通訊將斷開;如果合法性驗證通過,將繼續進行第四步;
- 使用者端隨機產生一個用於後面通訊的“對稱密碼”,然後用伺服器的公鑰(伺服器的公鑰從步驟②中的伺服器的證照中獲得)對其加密,然後將加密後的“預主密碼”傳給伺服器;
- 如果伺服器要求客戶的身份認證(在握手過程中為可選),使用者可以建立一個隨機數然後對其進行資料簽名,將這個含有簽名的隨機數和客戶自己的證照以及加密過的“預主密碼”一起傳給伺服器;
- 如果伺服器要求客戶的身份認證,伺服器必須檢驗客戶證照和簽名隨機數的合法性,具體的合法性驗證過程包括:客戶的證照使用日期是否有效,為客戶提供證照的CA是否可靠,發行CA 的公鑰能否正確解開客戶證照的發行CA的數字簽名,檢查客戶的證照是否在證照廢止列表(CRL)中。檢驗如果沒有通過,通訊立刻中斷;如果驗證通過,伺服器將用自己的私鑰解開加密的“預主密碼”,然後執行一系列步驟來產生主通訊密碼(客戶端也將通過同樣的方法產生相同的主通訊密碼);
- 伺服器和客戶端用相同的主密碼即“通話密碼”,一個對稱金鑰用於SSL協議的安全資料通訊的加解密通訊。同時在SSL通訊過程中還要完成資料通訊的完整性,防止資料通訊中的任何變化;
- 客戶端向伺服器端發出資訊,指明後面的資料通訊將使用的步驟⑦中的主密碼為對稱金鑰,同時通知伺服器客戶端的握手過程結束;
- 伺服器向客戶端發出資訊,指明後面的資料通訊將使用的步驟⑦中的主密碼為對稱金鑰,同時通知客戶端伺服器端的握手過程結束;
- 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;
}
};
複製程式碼