第一.http 通信協議的基本原理
一次 HTTP 請求的通信流程
流程圖
DNS: (Domain Name System)服務是和 HTTP 協議一樣位於應用層的協議。它提供域名到 IP 地址之間的解析服務, 用戶通常使用主機名或域名來訪問對方的計算機,
而不是直接通
過 IP 地址訪問。因為與 IP 地址的一組純數字相比,用字母配合數字的表示形式來指定計算機名更符合人類的記憶習慣,。DNS 協議提供通過域名查找 IP 地址,
或逆向從IP 地址反查域名的服務
第二.HTTP 通信協議的組成
HTTP 協議是基於應用層的協議,並且在傳輸層使用的 TCP 的可靠性通信協議。合協議的定義:協議是兩個需要通過網絡通信的程序達成的一種約定,
它規定了報文的交換方式和包含的意義
1.請求 URI 定位資源
URL (Uniform Resource Locator),統一資源定位符 ,用於描述一個網絡上的資源,具體格式是
URI 用字符串標識某一互聯網資源,而 URL 表示資源的地點(互聯網上所處的位置)。可見 URL 是 URI 的子集。
http://www.gupaoedu.com:80/java/index.html?name=mic#head
schema://host[:port#]/path/.../?[url-params]#[ query-string]
scheme 指定應用層使用的協議(例如:http, https, ftp)
host HTTP 服務器的 IP 地址或者域名
port# HTTP 服務器的默認端口是 80,這種情況下端口號可以省略。如果使用了別的端口,必須指明,例如 http://www.cnblogs.com:8080/
path 訪問資源的路徑
query-string 查詢字符串
# 片段標識符(使用片段標識符通常可標記出已獲取資源中的子資源(文檔內的某個位置))
通過這個 url 地址,我們就可以讀到,當前用戶要使用 http 協議訪問指定服務器上對應進程中的資源,並且攜帶了請求參數。
2.MIMETYPE
服務器根據用戶請求的資源找到對應的文件以后,會返回一個資源給到客戶端瀏覽器,瀏覽器會對這個資源解析並且渲染。但是服務器上的資源類型有很多,比如圖片類型、視頻類型、
Js、Css、文本等。瀏覽器如何識別當前類型做不同的渲染呢?MIME Type:是描述消息內容類型的因特網標准,
常見的幾種類型
文本類型:text/html,text/plain,text/css,application/xhtml+xml,application/xml
圖片文件:image/jpeg,image/gif,image/png
視頻文件:video/mpeg,video/quicktime
可以通過兩種方式來設置文件的渲染類型,第一種是 Accept,第二種是 Content-Type
Accept: 表示客戶端希望接受的數據類型,即告訴服務器我需要什么媒體類型的數據,此時服務器應該根據 Accept 請求頭生產指定媒體類型的數據
Content-Type: 表 示 發 送 端 發 送 的 實 體 數 據 類 型 , 比 如 我 們 應 該 寫 過 類 似 的 :resposne.setContentType(“application/json;charset=utf-8”)的代碼,表示服務端返回的數據格式是 json。
如果 Accept 和 Content-Type 不一致,假如說 Accept 要接收的類型是 image/gif,但是服務端返回的數據是 text/html,那么瀏覽器將會無法解析。
3.狀態碼
如果用戶訪問的地址沒問題,或者服務器也能正常解析及處理當前用戶的請求,那就能夠返回正確的信息給到客戶端。但是如果用戶訪問的地址有問題,或者服務端在解析用戶請求以
及處理請求邏輯時出現問題,怎么辦呢?瀏覽器應該怎么告訴用戶當前是處理失敗的呢?因此這里就涉及到一個狀態碼的概念狀態碼的職責是當客戶端向服務端發送請求時,
描述服務端返回的請求處理結果,通過狀態
碼,瀏覽器可以知道服務器是正常處理請求還是出現了錯誤
常見的錯誤碼:
200:一切正常
301:永久重定向
404:請求資源不存在
500:服務端內部錯誤
有了狀態碼,在用戶訪問某個網站出現非正常狀態時,瀏覽器就可以很友好的提示用戶
4.請求方式
GET:一般是用於客戶端發送一個 URI 地址去獲取服務端的資源(一般用於查詢操作),Get
不支持的傳輸數據有限制,具體限制由瀏覽器決定
POST:一般用戶客戶端傳輸一個實體給到服務端,讓服務端去保存(一般用於創建操作)
PUT:向服務器發送數據,一般用於更新數據的操作
DELETE:客戶端發起一個 Delete 請求要求服務端把某個數據刪除(一般用於刪除操作)
HEAD:獲得報文首部、OPTIONS:詢問支持的方法、TRACE:追蹤路徑、CONNECT:用隧道協議連接代理
定義REST 這個架構風格理由:
1. 隨着服務化架構的普及,http 協議的使用頻率越來越高
2. 很多人在錯誤的使用 http 協議定義接口,比如各種各樣的命名,什么 getUserInfoById,
deleteById 之類的、有狀態和無狀態請求混用。
3. 對於 http 協議本身提供的規則並沒有很好的利用,
所以,為了更好的解決這些問題,干脆就定義一套規則,這套規則並沒有引入新的東西,
無非就是對 http 協議本身的使用做了一些約束,比如說
1. REST 是面向資源,每一個 URI 代表一個資源
2. 強調無狀態化,服務器端不能存儲來自某個客戶的某個請求中的信息,並在該客戶的其他請求中使用
3. 強調 URL 暴露資源時,不要在 URI 中出現動詞
4. 合理的利用 http 狀態碼、請求方法。
5.http協議的完整組成
http 協議包含兩個報文,一個是請求報文,一個是響應報文
請求報文
請求報文格式包含三個部分,(起始行、首部字段、主體)
響應報文
響應的報文格式也是一樣,分為三部分
6.http協議的擴展
傳輸的文件過大
服務器上返回的資源文件比較大,比如有些 js 文件大小可能就有幾兆。文件過大就會影響傳輸的效率,同時也會帶來帶寬的消耗。怎么辦呢?
1. 常見的手段是,對文件進行壓縮,減少文件大小。那壓縮和解壓縮的流程怎么實現呢?
首先服務端需要能支持文件的壓縮功能,其次瀏覽器能夠針對被壓縮的文件進行解壓縮。瀏覽器可以指定 Accept-Encoding 來高速服務器我當前支持的編碼類型Accept-Encoding:gzip,deflate
那服務端會根據支持的編碼類型,選擇合適的類型進行壓縮。常見的編碼方式有:gzip/deflate
2. 分割傳輸
在傳輸大容量數據時,通過把數據分割成多塊,能夠讓瀏覽器逐步顯示頁面。這種把實體主
體分塊的功能稱為分塊傳輸編碼(Chunked Transfer Coding)。
每次請求都要建立連接嗎?
在最早的 http 協議中,每進行一次 http 通信,就需要做一次 tcp 的連接。而一次連接需要進行 3 次握手,這種通信方式會增加通信量的開銷。
所以在 HTTP/1.1 中改用了持久連接,就是在一次連接建立之后,只要客戶端或者服務端沒有明確提出斷開連接,那么這個 tcp 連接會一直保持連接狀態
持久連接的一個最大的好處是:大大減少了連接的建立以及關閉時延。HTTP1.1 中有一個 Transport 段。會攜帶一個 Connection:Keep-Alive,表示希望將此條連接
作為持久連接。HTTP/1.1 持久連接在默認情況下是激活的,除非特別指明,否則 HTTP/1.1 假定所有的連接都是持久的,要在事務處理結束之后將連接關閉,
HTTP/1.1 應用程序必須向報文中顯示地添加
一個 Connection:close 首部。HTTP1.1 客戶端加載在收到響應后,除非響應中包含了 Connection:close 首部,不然 HTTP/1.1連接就仍然維持在打開狀態。但是,客戶端和服務器仍然可以隨時關閉空閑的連接。不發送
Connection:close 並不意味這服務器承諾永遠將連接保持在打開狀態。管道化連接: http/1.1 允許在持久連接上使用請求管道。以前發送請求后需等待並收到響應,才能發送下一個請求。管線化技術出現后,不用等待響應亦可直接發送下一個請求。這樣就能夠做到同時並行發送多個請求,而不需要一個接一個地等待響應了。
第三.Http 協議的特點
3.1http無狀態協議
HTTP 協議是無狀態的,什么是無狀態呢?就是說 HTTP 協議本身不會對請求和響應之間的通信狀態做保存。但是現在的應用都是有狀態的,如果是無狀態,那這些應用基本沒人用,訪問一個
電商網站,先登錄,然后去選購商品,當點擊一個商品加入購物車以后又提示你登錄。這種用戶體驗根本不會有人去使用。
3.2客戶端支持的cookie
Http 協議中引入了 cookie 技術,用來解決 http 協議無狀態的問題。通過在請求和響應報文中寫入 Cookie 信息來控制客戶端的狀態;Cookie 會根據從服務器端發送的響應報文內的一
個叫做 Set-Cookie 的首部字段信息,通知客戶端保存 Cookie。當下次客戶端再往該服務器發送請求時,客戶端會自動在請求報文中加入 Cookie 值后發送出去。
3.3服務端支持session
服務端是通過什么方式來保存狀態的呢? 在基於 tomcat 這類的 jsp/servlet 容器中,會提供session 這樣的機制來保存服務端的對象狀態,服務器使用一種類似於散列表的結構來保存信息,當程序需要為某個客戶端的請求創建一個 session 的時候,服務器首先檢查這個客戶端的請求是否包含了一個 session 標識- session id;如果已包含一個 session id 則說明以前已經為客戶端創建過 session,服務器就按照 sessionid 把這個 session 檢索出來使用(如果檢索不到,會新建一個);如果客戶端請求不包含 sessionid,則為此客戶端創建一個 session 並且生成一個與此 session相關聯的 session id, session id 的值是一個既不會重復,又不容易被找到規律的仿造字符串,這個 session id 將會返回給客戶端保存
第四Https 協議基本分析
由於 HTTP 協議在通信過程中,是基於明文通信,並且底層是基於 TCP/IP 協議進行通信,那么按照 TCP/IP 協議族的工作機制,通信內容在所有的通信線路上都有可能遭到攔截和竊取。
竊取這個過程其實很簡單,通過抓包工具 Wireshark 就可以截獲請求和響應的內容。
https 安全傳輸協議
由於 HTTP 協議通信的不安全性,所以人們為了防止信息在傳輸過程中遭到泄漏或者篡改,就想出來對傳輸通道進行加密的方式 https。
https 是一種加密的超文本傳輸協議,它與 HTTP 在協議差異在於對數據傳輸的過程中,https對數據做了完全加密。由於 http 協議或者 https 協議都是處於 TCP 傳輸層之上,同時網絡協
議又是一個分層的結構,所以在 tcp 協議層之上增加了一層 SSL(Secure Socket Layer,安全層)或者 TLS(Transport Layer Security) 安全層傳輸協議組合使用用於構造加密通道;
第五.https的設計過程
從第一個消息開始
客戶端 A 向服務端 B 發送一條消息,這個消息可能會被攔截以及篡改,如何做到 A 發送給 B 的數據包,及時被攔截了,也沒辦法得知消息內容並且也不能查看呢?
利用對稱加密
要做到消息不能被第三方查看以及篡改,那么第一想法就是對內容進行加密,同時,該消息還需要能被服務端進行解密。所以我們可以使用對稱加密算法來實現,密鑰 S 扮演着加密和
解密的角色。在密鑰 S 不公開的情況下,就可以保證安全性?
對稱加密的安全性

會存在多個客戶端和服務端產生連接,而這個客戶端也許是一個潛伏者,如果他也有對稱密鑰 S,那相當於上面的方案是不可行的?如果服務端和每個客戶端通信的時候使用不同的加
密算法呢?
是基於一個網絡傳輸的情況下去動態分配密鑰,是不安全的
非對稱加密
非對稱加密算法的特點是:私鑰加密后的密文,只要有公鑰,都能解密,但是公鑰加密后的密文,只有私鑰可以解密。私鑰只有一個人有,而公鑰可以發給所有人
這樣就可以保證 A/B 向服務器端方向發送的消息是安全的。
公鑰
使用非對稱加密算法,那么如何讓 A、B 客戶端安全地持有公鑰?
兩種方案:
1. 服務器端將公鑰發送給每一個客戶端
2. 服務器端將公鑰放到一個遠程服務器,客戶端可以請求到 (多了一次請求,還得解決公鑰放置問題)
方案一似乎不可行,因為,傳輸過程又是不安全的?公鑰可能會被調包
通過第三方機構拿到公鑰,原理:服務端把需要傳遞給客戶端的公鑰,通過第三方機構提供的私鑰對公鑰內容進行加密后,再傳遞給客戶端? 通過第三方機構私鑰對服務端公鑰加密以后的內容,就是一個簡陋版本的“數字證書”。這個幀數中包含【服務器公鑰】


客戶端拿到這個證書以后,因為證書是第三方機構使用私鑰加密的。客戶端必須要有第三方機構提供的公鑰才能解密證書。這塊又涉及到第三方機構的公鑰怎么傳輸?(假設是先內置
在系統中)以及還有一個問題,第三方機構頒發的證書是面向所有用戶,不會只針對一家發放。如果不法分子也去申請一個證書呢?
如果不法分子也申請了證書,那它可以對證書進行調包。客戶端在這種情況下是無法分辨出收到的是你的證書,還是中間人的。因為不論是中間人的、還是你的證書都能使用第三方機構的公鑰進行解密。

驗證證書的有效性
證書上寫了如何根據證書的內容生成證書編號。客戶端拿到證書后根據證書上的方法自己生成一個證書編號,如果生成的證書編號與證書上的證書編號相同,那么說明這個證書是真實
的。這塊有點類似於 md5 的驗證,我們下載一個軟件包,都會提供一個 md5 的值,我們可以拿到這個軟件包以后通過一個第三方軟件去生成一個 md5 值去做比較,是不是一樣如果一樣表示這個軟件包沒被篡改過
對服務端的數據進行 MD5 算法得到一個MD5 的值,生成證書編號,使用第三方機構的私鑰對這個證書編號進行加密,並且會在證書中添加證書編號的生成算法
瀏覽器內置的 CA 公鑰可以解密服務端 CA 私鑰加密的證書,通過瀏覽器內置的 CA 證書的證書編號算法對服務端返回的證書編號進行驗簽
瀏覽器和操作系統都會維護一個權威的第三方機構列表(包括他們的公鑰)因為客戶端接收到的證書中會些頒發機構,客戶端就根據這個辦法機構的值在本地找到響應的公鑰
說到這里,我想大家一定知道,證書就是 HTTPS 中的數字證書,證書編號就是數字簽名,而第三方機構就是數字證書的簽發機構(CA)
第五.Https 原理分析
HTTPS 證書的申請過程
1. 服務器上生成 CSR 文件(證書申請文件,內容包括證書公鑰、使用的 Hash 簽名算法、申
請的域名、公司名稱、職位等信息)

2. 把 CSR 文件和其他可能的證件上傳到 CA 認證機構,CA 機構收到證書申請之后,使用申請中的 Hash 算法,對部分內容進行摘要,然后使用 CA 機構自己的私鑰對這段摘要信息進行
簽名(相當於證書的唯一編號)
3. 然后 CA 機構把簽名過的證書通過郵件形式發送到申請者手中。
4. 申請者收到證書之后部署到自己的 web 服務器中
客戶端請求交互流程
1. 客戶端發起請求(Client Hello 包)
a) 三次握手,建立 TCP 連接
b) 支持的協議版本(TLS/SSL)
c) 客戶端生成的隨機數 client.random,后續用於生成“對話密鑰”
d) 客戶端支持的加密算法
e) sessionid,用於保持同一個會話(如果客戶端與服務器費盡周折建立了一個 HTTPS 鏈接,剛建完就斷了,也太可惜)
2. 服務端收到請求,然后響應(Server Hello)
a) 確認加密通道協議版本
b) 服務端生成的隨機數 server.random,后續用於生成“對話密鑰”
c) 確認使用的加密算法(用於后續的握手消息進行簽名防止篡改)
d) 服務器證書(CA 機構頒發給服務端的證書)
3. 客戶端收到證書進行驗證
a) 驗證證書是否是上級 CA 簽發的, 在驗證證書的時候,瀏覽器會調用系統的證書管理器接口對證書路徑中的所有證書一級一級的進行驗證,只有路徑中所有的證書都是受信的,
整個驗證的結果才是受信
b) 服務端返回的證書中會包含證書的有效期,可以通過失效日期來驗證 證書是否過期
c) 驗證證書是否被吊銷了
d) 前面我們知道 CA 機構在簽發證書的時候,都會使用自己的私鑰對證書進行簽名證書里的簽名算法字段 sha256RSA 表示 CA 機構使用 sha256 對證書進行摘要,然后使用 RSA 算法對摘要進行私鑰簽名,而我們也知道 RSA 算法中,使用私鑰簽名之后,只有公鑰才能進行驗簽。
e) 瀏覽器使用內置在操作系統上的 CA 機構的公鑰對服務器的證書進行驗簽。確定這個證書是不是由正規的機構頒發。驗簽之后得知 CA 機構使用 sha256 進行證書摘要,然后客戶端再使用 sha256 對證書內容進行一次摘要,如果得到的值和服務端返回的證書驗簽之后的摘要相同,表示證書沒有被修改過
f) 驗證通過后,就會顯示綠色的安全字樣
g) 客戶端生成隨機數,驗證通過之后,客戶端會生成一個隨機數 pre-master secret,客戶
端根據之前的:Client.random + sever.random + pre-master 生成對稱密鑰然后使用證書中的公鑰進行加密,同時利用前面協商好的 HASH 算法,把握手消息取 HASH 值,
然后用 隨機數加密 “握手消息+握手消息 HASH 值(簽名)” 並一起發送給服務端 (在
這里之所以要取握手消息的 HAS值,主要是把握手消息做一個簽名,用於驗證握手
消息在傳輸過程中沒有被篡改過。)
4. 服務端接收隨機數
a) 服務端收到客戶端的加密數據以后,用自己的私鑰對密文進行解密。然后得到
client.random/server.random/pre-master secret, HASH 值,並與傳過來的 HASH 值做
對比確認是否一致。
b) 然后用隨機密碼加密一段握手消息(握手消息+握手消息的 HASH 值 )給客戶端
5. 客戶端接收消息
a) 客戶端用隨機數解密並計算握手消息的 HASH,如果與服務端發來的 HASH 一致,此
時握手過程結束,
b) 之 后 所 有 的 通 信 數 據 將 由 之 前 交 互 過 程 中 生 成 的 pre master secret /client.random/server.random 通過算法得出 session Key,作為后續交互過程中的對稱密鑰

