前言
最近一段時間在空閒之餘拜讀了一下《圖解HTTP協議》,收貨頗豐。以前不懂的地方在讀完這本書之後,豁然開朗。於是花了一些時間總結一下,其中我也查閱了一些其他資料來補充進去,希望這篇文章可以給大家帶來幫助。如果各位覺得我寫的還不錯的話,還望大家多多收藏點在支援哦!
網路基礎
TCP/IP分層
TCP/IP體系有人把它分為四層也有人把它分為五層,不同書有著不同的分法。五層和四層的區別就在於五層的資料鏈路層和物理層對應著四層的網路介面層。二者都對,不必要糾結,瞭解一下即可。如果按照協議劃分的話,物理層是沒有必要單獨劃分出來的,畢竟物理層是沒有協議的。本篇文章採用的是四層結構。
名稱 | 作用 |
---|---|
應用層 | HTTP、FTP、DNS等協議就位於該層上,用於向使用者提供應用服務. |
傳輸層 | 該層最主要的協議就是TCP和UDP協議,為應用層實體提供端到端的通訊功能。 |
網路層 | 該層的核心就是IP協議,規定了通過什麼樣子的路徑到達對方計算機,並把資料包傳送給對方。ARP、RARP協議也位於該層。 |
網路介面層 | 負責接收IP資料包並通過網路傳送,或者從網路上接收物理幀,抽出IP資料包,交給網路層。該層常見的協議就是Ethernet 802.3、Token Ring 802.5。 |
TCP/IP通訊傳輸流
利用TCP/IP協議族進行通訊時,會通過分層順序與對方進行通訊。傳送端從應用層往下走,接收端則從下往應用層向上走。傳送端在層與層之間傳輸資料時,每經過一層時就會打上一個該層對應的首部資訊。反之,接收端在層與層傳輸資料時,每經過一層時會把對應的首部資訊剔除。
TCP協議的三次握手
TCP協議位於傳輸層,其作用就是提高可靠的位元組流服務。為了可以準確無誤地將資料送達到目標處,TCP協議採用了三次握手策略來保證其可靠性。其整體流程如下:
傳送端首先會傳送一個帶SYN標誌的資料包給對方。接收端收到後,回傳一個帶有SYN/ACK標誌的資料包以示傳達確認資訊。最後,傳送端再回傳一個帶ACK標誌的資料包,代表“握手”結束。
對於TCP協議的三次握手,相信有不少小夥伴們有著這樣一個疑問。為什麼一定要三次握手才可以保證其可靠性,一次兩次不行嗎?針對這個問題,我們舉個簡單的例子來說明一下。
場景描述: 有一對好基友在高中畢業之後就失去了聯絡,十年過去了,基友甲通過某些方式弄到了基友乙的聯絡方式,準備打個電話給基友乙,敘敘舊,重燃當年撿肥皂的基情。
場景分析: 場景中基友甲就是客戶端,而基友乙就是服務端。現在這麼一個問題,怎樣保證基友甲和基友乙可以順利地重拾當年的肥皂情?從基友甲的角度來說,我得確定我電話對面的是基友乙。從基友乙的角度來說,我得確定我電話對面的是基友甲。只有雙方都能確定電話那一頭就是對方,那麼這個肥皂情才能重拾。接下來我們以這個維度去分析這三次握手。
第一次握手: 基友甲撥通電話,說了一句“喂,請問是基友乙嗎?”(這就好比於客戶端傳送一個帶SYN標誌的資料包給服務端)。這句話發出,基友甲還不能確定對方就是基友乙,同樣基友乙也不能確定對方是基友甲。
第二次握手: 基友乙接起電話,說了一句“我是基友乙,請問你是哪位?”(這就好比於服務端回傳一個帶有SYN/ACK標誌的資料包,確認資訊)。這句話一發出,基友甲就可以確定了對方是基友乙,但此時基友乙還不能確定對方是基友甲。
第三次握手: 基友甲確定了電話另一頭是基友乙,非常興奮,回了一句“你不認識我了嗎?我是基友甲啊”(這就好比於傳送端再回傳一個帶ACK標誌的資料包)。這一句話一發出,那麼基友乙便可根據這句話確認了電話那一頭就是基友甲。此時,甲乙雙方均可互相確認是對方。滿足可以重拾肥皂情的條件,可以開始重拾基友情。
通過我們舉例這個肥皂情來對三次握手的分析,我們發現只有三次握手才能保證通訊的可靠性,兩次是不可以的保證的。希望我這個舉例可以加深大家對三次握手的理解。 ##TCP的四次分手 上面我們講了TCP的建立連線,連線之後必然就要遇到斷開連線的問題。TCP是需要四次分手才能斷開連線的。估計會有不少人不理解這個問題,一次客戶端傳送斷開連線請求,還有一次服務端收到後回傳一個確認資訊。兩次就可以斷了,為什麼還要四次?說到這裡,我們要弄清楚一件事,TCP協議是採用的是全雙工模式,什麼叫全雙工?全雙工簡單來說就是可以同時進行雙向的資料傳輸。因此,客戶端不僅僅是傳送端,也可以是接收端。而服務端不僅僅是接收端,也可以是傳送端。所以,我們把TCP建立的連線拆成兩個單向連線。如下:
1. 客戶端是傳送端,服務端是接收端。
2. 服務端是傳送端,客戶端是接收端。
每個連線斷開,我們需要一個傳送端傳送斷開請求,接收端確認斷開請求,兩次分手。兩個連線便是兩次,總共就是四次分手。整個流程如下:
第一次分手: 客戶端傳送FIN報文段給服務端,用來斷開客戶端到服務端的連線。此時,客戶端處於FIN_WAIT_1狀態,即沒有資料要傳送了,在等待服務端的斷開連線的確認。
第二次分手: 服務端接受到來自客戶端的FIN報文段之後,回傳了一個ACK報文段,同意了客戶端的斷開連線的請求。此時服務端進入CLOSE-WAIT狀態,等待關閉客戶端關閉連線。客戶端在接收到這個來自服務端回傳的ACK報文段時,客戶端關閉向服務端傳送資料的連線,客戶端此時進行FIN_WAIT_2狀態。
第三次分手: 斷開客戶端到服務端的連線之後,服務端傳送一個FIN報文段給客戶端,請求斷開服務端到客戶端的連線。此時服務端進入LAST_ACK狀態,等待客戶端的確認。
第四次分手: 客戶端接收到來自服務端的FIN報文之後,回傳一個ACK報文段,同意連線關閉請求。此時,客戶端進入TIME_WAIT狀態。服務端接收到來自客戶端的ACK報文段之後,便關閉服務端向客戶端傳送資料的連線,服務端就進入了CLOSED狀態。而客戶端此時還在等待狀態,那怎麼讓客戶端也進入CLOSED狀態呢?很簡單,客戶端在等待兩個MSL時間之後,它會自動進入CLOSED狀態。
URI和URL的區別
一說到URL和URI,那真的就是老虎老鼠傻傻分不清楚,應該有不少小夥伴們會弄混它們。URL叫做統一資源定位符,而URI叫做統一資源識別符號。雖然說這兩者名字很相似,但是區分他們確實很簡單。URL和URI我們可以類比成郵政編碼和收件地址。URL的範圍是要大於URI的。我們以淘寶的例子來說,https://www.taobao.com/這個域名就是URL,而每個商品的地址就是一個URI。
很多AJAX工具中,地址引數名稱用的是url,例如jquery中的$.ajax方法中用的就是url,但是我們得弄清楚HTTP請求的地址是URI,而不是URL。 #簡單的HTTP協議
HTTP請求方法
名稱 | 描述 | 最低支援協議版本 |
---|---|---|
GET | 請求伺服器上的某一資源。 | 1.0 |
POST | 向指定資源提交資料進行處理請求,資料包含在請求體中。 | 1.0 |
HEAD | 用於確認URI的有效性及資源更新的日期時間,不返回報文主體,只返回報文文首部。 | 1.0 |
PUT | 向用來傳輸檔案,將檔案內容放進報文主體中,儲存到URI指定位置上。 | 1.1 |
DELETE | 與PUT相反,請求URI刪除指定資源。 | 1.1 |
OPTIONS | 查詢針對請求URI指定的資源支援的方法。 | 1.1 |
TRACE | 用於追蹤路徑。傳送請求時,首部欄位Max-Forwards會指定一個數值,每經過一個伺服器之後,該數值減1。當該數值為0時,停止傳輸,最後接收到的伺服器響應。 | 1.1 |
CONNECT | 用於在與代理伺服器通訊時建立隧道,實現用隧道協議進行TCP通訊。 | 1.1 |
HTTP協議1.1
持久連線
在1.0版本中,每進行一次HTTP通訊就要斷開一次TCP連線。TCP連線的新建成本很高,頻繁地開啟關閉會極大地增加開銷,影響效能。於是在1.1版本中引入持久連線的方法。其特點就是任意一端沒有明確提出斷開連線,則保持TCP連線的狀態。
管道機制
在1.1版本之前,在一個TCP連線中,客戶端傳送一個請求之後,必須等待伺服器響應之後才能傳送下一個請求,不能同時並行傳送多個請求。1.1版本的管道機制解決這個問題,客戶端不必等待服務端響應之後再傳送請求了,可以並行傳送多個請求。
增加HOST欄位
1.0版本中,認為每臺物理伺服器對應唯一的IP地址。所以,在1.0版本中是沒有主機名這個概念的。但隨著Web技術的發展,一臺物理伺服器可以存在多個虛擬主機,他們共享同一個IP地址。為了解決這個問題,HOST欄位應運而生。
分塊傳輸編碼
在1.1版本中,在一個TCP連線中存在多個響應。如何區分資料包對應哪個響應就成了問題。在1.1版本中就出現Content-Length這個欄位,標記本次響應的資料長度。
例如:Content-Length: 2333
告訴客戶端本次回應的資料長度為2333個位元組,後面的位元組就不屬於這次響應
在使用Content-Length的前提條件就是得知道整個資料長度才行。因此,產生出一個資料塊的時候是不能立即傳輸給客戶端,得等所有資料產生完畢才能傳送。這中間的等待時間勢必會影響效能。為解決這個弊端,1.1版本中提出了“分塊傳輸編碼”的解決方案,在響應頭會有一個Transfer-Encoding這個欄位,告訴客戶端這次響應是由數量未定的資料塊組成,每一個非空資料塊都有一個16進位制的數值來標記其長度。最後以長度為0的資料塊來表示這次響應的資料傳送完畢。
加入100狀態碼
隨著Web應用的複雜度不斷增加,往往服務端會加入許可權控制。如果客戶端傳送一個HTTP請求,請求體重帶著大量資料過來,結果服務端因為其沒有許可權給它打回了。那麼這就造成無謂的開銷。100狀態碼引入之後,客戶端事先傳送一個帶部分請求體的HTTP請求,如果服務端的響應碼為100,客戶端會帶上剩餘的請求體再次傳送HTTP請求。反之,則取消後續的帶有剩下的請求體的HTTP請求。
HTTP協議2.0
二進位制分幀
在HTTP協議2.0中,應用層和傳輸層之間會多一個二進位制分幀層。在二進位制分幀層上,HTTP 2.0 會將所有傳輸的資訊分割為更小的幀,並對它們採用二進位制格式的編碼 。之前HTTP1.x版本中的HTTP報文首部資訊會被封裝到Headers幀,而我們的HTTP報文主體則封裝到Data幀裡面。原先我們是以HTTP報文為單位傳輸的,現在HTTP報文被拆成了多個幀的形式,並且這些幀可以亂序傳送,我們只需根據每個幀首部的流識別符號就可以重新完成組裝。這樣極大提升了HTTP的效能。
多路複用
多路複用允許同時通過單一的 TCP 連線 連線發起多重的請求-響應訊息。在 HTTP/1.1 協議中 客戶端在同一時間,針對同一域名下的請求有一定數量限制。超過限制數目的請求會被阻塞。針對這一個情況,2.0中採用了多路複用的機制,通過單一的 HTTP/2 連線可以發起多重的請求-響應報文,這樣就不用依賴多個TCP連線了。
首部壓縮
每次HTTP請求都會有一個請求首部,這個首部放到一些重要資訊,比如Cookie、User Agent之類的欄位,這些欄位每次請求都是一樣的,但還必須要帶上。這就造成了一些不必要的浪費。2.0中就優化這一點,引入首部壓縮機制,客戶端和服務端會維護同樣一張首部資訊表,每次請求只要傳送索引號就可以了,不必帶上請求首部上冗餘的key-value,極大地減少了不必要的浪費。
服務端推送
在2.0之前的版本中,服務端是屬於被動一方,只有客戶端傳送請求,服務端才能傳送資源。2.0協議中,服務端可以主動地向客戶端傳送資源。例如:客戶端請求一個html,裡面所需要的js和css完全不需要客戶端解析完html之後再去請求這些內容那麼麻煩,服務端可以在客戶端請求html的時候一起回傳過來。
使用Cookie管理狀態
HTTP是一種無狀態協議,就是它不會對之前發生過的請求和響應的狀態進行管理。也就是說,無法根據之前的狀態進行本次的請求處理。例如,我們訪問一個需要登入驗證的網頁,我們登入完成之後沒有對其登入狀態進行管理的話,那麼每次請求新頁面都需要重新登陸一下。這樣體驗就很差。為了解決這一尷尬,於是引入Cookie這個技術。
在沒有Cookie資訊狀態下: 客戶端傳送一個驗證請求給服務端,服務端驗證通過之後,將驗證資訊新增在Cookie中,並將其返回給客戶端。客戶端拿到這個Cookie就將其存在本地。
第二次以後(存有Cookie資訊狀態)的請求: 客戶端再次請求時會新增之前存在本地的Cookie傳送給伺服器,伺服器根據Cookie帶過來的驗證資訊,對其進行驗證,驗證通過直接返回資料,反之跳轉到登入頁。
既然Cookie是存在瀏覽器端的,所以js是可以訪問Cookie的,我們本地也可以用Cookie去做一些儲存的操作,下面附上js中對Cookie的操作程式碼。
//寫入Cookie
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
var expires = "expires="+d.toUTCString();
document.cookie = cname + "=" + cvalue + "; " + expires;
}
//讀取Cookie
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1);
if (c.indexOf(name) != -1) return c.substring(name.length, c.length);
}
return "";
}
//清除cookie
function clearCookie(name) {
setCookie(name, "", -1);
}
複製程式碼
HTTP報文內的HTTP資訊
HTTP報文結構
用於HTTP協議互動的資訊被稱為HTTP報文。請求端(客戶端)的HTTP報文叫做請求報文,響應端(伺服器端)的叫做響應報文。HTTP報文字身是由多行(用CR+LF作換行符)資料構成的字串文字。 HTTP報文大致可分為請求行/響應行、報文首部和報文主體兩塊。兩者由最初出現的空行(CR+LF)來劃分。關於報文首部是我們重點需要掌握的,文章的後面我們會有較大的篇幅去闡述,這邊就帶過。
請求報文的請求行主要三個部分組成:請求方法、URI地址和HTTP協議版本號。
例如:
POST https://segmentfault.com/api/article/draft/save HTTP/1.1
複製程式碼
響應報文的響應行主要三個部分組成:HTTP協議版本號、HTTP狀態碼和狀態描述。
例如:
HTTP/1.1 200 OK
複製程式碼
##編碼提升傳輸速率 HTTP在傳輸資料時可以按照資料原貌直接傳輸,但也可以在傳輸過程中通過編碼提升傳輸速率。通過在傳輸時編碼,能有效地處理大量的訪問請求。但是,編碼的操作需要計算機來完成,因此會消耗更多的CPU等資源。
壓縮傳輸的內容編碼
向待傳送郵件內增加附件時,為了使郵件容量變小,我們會先用ZIP壓縮檔案之後再新增附件傳送。HTTP協議中有一種被稱為內容編碼的功能也能進行類似的操作。
內容編碼指明應用在實體內容上的編碼格式,並保持實體資訊原樣壓縮。內容編碼後的實體由客戶端接收並負責解碼。
常用的內容編碼格式:
- gzip
- compress
- deflate
- identity
分割傳送的分塊傳輸編碼
在本文介紹http協議1.1版本的時候,我們就講過這些。大家可以再次結合下面這張圖來理解整個流程。
傳送多種資料的多部分物件集合
我們傳送郵件時,可以往郵件附件裡新增圖片、視訊等資料。這個功能得益於MIME機制,它允許郵件處理文字、圖片、視訊等不同型別的資料。MIME採用就是多部分物件集合的方法來容納多份不同型別的資料。
我們在HTTP協議框架下上傳圖片或文字檔案等資料時,也採用了這個多部分物件集合的方法。
多部分物件集合包含的物件如下:
-
multipart/form-data
web表單上傳檔案時使用
-
multipart/byteranges 狀態碼206響應報文包含了多個範圍的內容時使用。 例如:
content-type:multipart/form-data; boundary=WebKitFormBoundary5V53Jp7BUFBGzu9B
// 注:boundary指定劃分多部分物件集合的起止符
複製程式碼
--WebKitFormBoundary5V53Jp7BUFBGzu9B
Content-Disposition: form-data; name="image"; filename="表二:價值觀評分表.numbers"
Content-Type: application/x-iwork-keynote-sffnumbers
--WebKitFormBoundary5V53Jp7BUFBGzu9B--
//開始的標記:--WebKitFormBoundary5V53Jp7BUFBGzu9B
//結束的標記:--WebKitFormBoundary5V53Jp7BUFBGzu9B--
複製程式碼
獲取部分內容的範圍請求
在很早之前網際網路技術還不是很發達的時候,比如我們下載一個稍微大一點的遊戲得時候,如果中途遇到什麼情況中斷了下載,就必須重新從零開始下載,那種痛在現在高速網路的時代是無法體會到,當然我也是無法體會到的,因為我還很年輕,哈哈哈!為了解決這種痛,範圍請求技術應運而生,中斷的下載便可得以恢復。
執行範圍請求時,會用到首部欄位Range來指定資源的byte範圍。
1.Range:bytes=5001-10000 5001~10000位元組
2.Range:bytes=5001- 從5001位元組之後全部的內容
3.Range:bytes=-3000,5000-7000 從0~3000位元組和5000~7000位元組的多重範圍
複製程式碼
針對這個範圍請求,我們上面所說到響應會返回狀態碼206。其中針對多重範圍的範圍請求時,響應會在用上首部欄位Content-Type:multipart/byteranges。
內容協商返回最合適的內容
在全球化的浪潮下,催生了一大批國際公司,像國際知名的視訊網站YOUTUBE,每天都有來自全球各地的網民登入這個網站。這樣面臨了一個問題,不同的國家他們的語言是不一樣的,YOUTUBE不可能對所有人都千篇一律,都用英文,那這樣就不符合這種國際知名大公司的形象。所以YOUTUBE也不可能這麼做,但是怎麼保證不同國家的網民可以對應不同語言的網站呢?為解決這個問題,內容協商機制就應運而生。如果我們使用的瀏覽器設定的語言是簡體中文,那麼我們訪問YOUTUBE的時候,則給我們顯示的網頁便是簡體中文的,以此類推。
內容協商機制是指客戶端和伺服器端就響應資源進行交涉,然後提供客戶端最合適的資源。
請求報文某些首部欄位作為判斷的基準,如下:
- Accept
- Accept-Charset
- Accept-Encoding
- Accept-Language
- Content-Language
內容協商技術有以下三種型別:
- 伺服器驅動協商 (由伺服器端進行內容協商的方式)
- 客戶端驅動協商 (由客戶端進行內容協商的方式)
- 透明協商 (由伺服器端和客戶端各自進行內容協商的方式)
返回結果的HTTP狀態碼
狀態碼的職責是當客戶端向伺服器端傳送請求時,描述返回的請求結果。藉助狀態碼,使用者可以知道伺服器端是正常處理了請求,還是出現了錯誤。這裡我們列舉常見的HTTP狀態碼,其他感興趣的可以自己去查閱。這裡我們附上完整的HTTP狀態碼的地址:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
1XX
100 Continue (繼續)
客戶端應當繼續傳送請求。這個臨時響應是用來通知客戶端它的部分請求已經被伺服器接收,且仍未被拒絕。客戶端應當繼續傳送請求的剩餘部分,或者如果請求已經完成,忽略這個響應。伺服器必須在請求完成後向客戶端傳送一個最終響應。
101 Switching Protocols (切換協議)
伺服器已經理解了客戶端的請求,並將通過Upgrade 訊息頭通知客戶端採用不同的協議來完成這個請求。在傳送完這個響應最後的空行後,伺服器將會切換到在Upgrade 訊息頭中定義的那些協議。
2XX
200 OK (成功)
表示從客戶端發來的請求在伺服器端被正常處理了
204 No Content (無內容)
伺服器成功處理了請求,但在返回的響應報文不含任何實體內容。比如:頁面上有一個a標籤,它的href屬性設定的是http-204.html,點選a標籤,正常情況下會跳轉到http-204.html。但是,如果http-204.html的響應碼是204,則頁面不跳轉,停留在當前頁。
206 Partial Content(部分內容)
該狀態碼在前面範圍請求的時候提到過,它表示客戶端進行了範圍請求,而伺服器成功執行了這部分的GET請求。響應報文中包含由Content-Range指定範圍的實體內容。
3XX
301 Moved Permanently (永久重定向)
該狀態碼錶示請求的資源已被分配了新的URI,以後應使用資源現在所指的URI。響應報文的首部欄位會用location欄位來標記新的URI。
302 Found (臨時重定向)
該狀態碼錶示本次請求的資源被臨時分配了新的URI。由於這樣的重定向是臨時的,客戶端應當繼續向原有地址傳送以後的請求。同樣,在響應報文的首部欄位location中也會標記本次被分配到的新的URI。
303 See Other (檢視其它位置)
該狀態碼錶示對應當前請求的響應可以在另一個URI上被找到,而且客戶端應當採用GET的方式訪問那個資源。這裡需要注意到的一點就是,303明確說明需要用GET方法獲取。跟上面一樣,在響應報文的首部欄位location中也會標記本次被分配到的新的URI。
304 Not Modified (未修改)
該狀態碼錶示所請求的資源未修改,伺服器返回此狀態碼時,不會返回任何資源。客戶端通常會快取訪問過的資源,通過提供一個頭資訊指出客戶端希望只返回在指定日期之後修改的資源。
307 Temporary Redirect (臨時重定向)
跟302 Found的含義是一樣的。為什麼會有307的出現?願意在於當303標準雖然禁止POST變成GET,但是實際情況卻經常發生,而307會嚴格遵守標準,不會把POST變成GET。在POST重定向中會很有用。
4XX
400 Bad Request (錯誤請求)
該狀態碼錶示由於包含語法錯誤,當前請求無法被伺服器理解,需重新修改再次傳送請求。在開發過程中,大家把400歸納到前端的問題,然後後端不理不睬的。這樣劃分是不對的,這裡語法錯誤大多數情況下並不是前端寫的有問題,而是前端某些欄位型別與後端沒有協同好,比如後端接受的是Long型別或者Date,而前端傳的String。又或者前端用json傳給後端,後端沒有做解析json的操作。這些問題很多情況下是前後端沒有協同好造成的,而不是單純是前端的問題。
401 Unauthorized (未授權)
該狀態碼錶示當前請求需要使用者驗證。該響應必須包含一個適用於被請求資源的WWW-Authenticate資訊頭用以詢問使用者資訊。
403 Forbidden (已禁止)
該狀態碼錶示對請求資源的訪問被伺服器拒絕了。這種在爬蟲過程中最常見,對方發現你正在爬他們網站的資料,然後對你的IP進行限制,就會造成403.
404 Not Found(未找到)
該狀態碼錶示伺服器上沒有找到請求的資源。這個大家經常會遇到,比如說手抖啦,打錯字母或者你訪問的網站被封啦之類的。
405 Method Not Allowed(方法禁用)
該狀態碼錶示請求行中指定的請求方法不能被用於請求相應的資源。該響應報文必須返回一個Allow頭資訊用以表示出當前資源能夠接受的請求方法的列表。
5XX
500 Internal Server Error(伺服器內部錯誤)
該狀態碼錶示伺服器在執行請求時發生了錯誤,可以是程式碼存在bug或者某些臨時性的故障。
503 Service Unavailable(服務不可用)
該狀態碼錶示伺服器暫時處於超負載或正在進行停機維護,現在無法處理請求。
HTTP首部
前面在提到HTTP報文的結構時,我們說過HTTP報文首部是非常重要的一塊,將單獨拎出來大篇幅地區講它。這裡我們將重點講這個問題。
首先我們要確定報文首部構成,報文首部可以請求首部(請求頭)和響應首部(響應頭)。其中,請求首部可拆分為請求首部欄位、通用首部欄位、實體首部欄位、其他擴充首部欄位,而響應首部可拆分成響應首部欄位、通用首部欄位、實體首部欄位、其他擴充首部欄位。由上我們可以得出:
報文首部=請求首部欄位/響應首部欄位+通用首部欄位+實體首部欄位+其他擴充首部欄位
下面我們將從請求首部欄位、響應首部欄位、通用首部欄位、實體首部欄位這四個部分入手,由於欄位過多,我們只挑選其中比較常見的首部欄位進行闡述。完整版的HTTP首部可以自行檢視,附上地址:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers ##請求首部欄位
Accept
該欄位用於指定客戶端接受哪些型別的資訊。
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
複製程式碼
上一個例子中的q=來額外表示權重值,用(;)進行分割。q的取值範圍為0~1(可精確到小數點後3位),且1為最大值。不指定權重q值時,預設權重為q=1.0。另外,可以用(*)作為萬用字元,指定任意型別資訊。
Accept-Charset
該欄位用於用於指定客戶端接受的字符集。
Accept-Charset:iso-8859-15,unicode-1-1;q=0.8
複製程式碼
這裡的q跟Accept的q的效果一致,不再贅述。萬用字元(*)也同樣一致。
Accept-Encoding
用於指定客戶端可接受的內容編碼。
Accept-encoding:gzip, deflate;q=0.9, br
複製程式碼
這裡同樣可以用q來表示其優先順序,與Accept相同。另外,萬用字元(*)同樣一致。
Accept-Language
用於指定客戶端可接受的自然語言集(指中文或英文等)。
Accept-Language:zh-CN,zh;q=0.9,en;q=0.8
複製程式碼
權重值q跟Accept一致,萬用字元(*)也一致。
Authorization
主要用於證明客戶端有權檢視某個資源。通常,想要通過伺服器認證的使用者代理會在接收到返回的401狀態碼響應後,會首部欄位Authorization加入請求中。
Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
複製程式碼
Expect
用於指定期望條件,並告知伺服器只有在滿足此期望條件的情況下才能妥善地處理請求。HTTP/1.1只規範了一個期望條件,即:
Expect: 100-continue
複製程式碼
伺服器會返回狀態碼100或者狀態碼417,返回100表示請求頭中的期望條件可以得到滿足,而417則表示不能滿足請求頭的期望條件。
From
用於告知伺服器傳送請求的使用者代理的實際使用者的電子郵件地址。比如說:如果你在執行一個機器人代理程式(比如爬蟲),那麼 Form 首部應該隨請求一起傳送,這樣的話,在伺服器遇到問題的時候,例如機器人代理髮送了過量的、不希望收到的或者不合法的請求,站點管理員可以聯絡到你。
From: webmaster@example.org
複製程式碼
Host
指明瞭伺服器的域名(用於區分虛擬主機),以及(可選的)伺服器監聽的TCP埠號。埠號是可選的,如果未指定埠,則會自動呼叫被請求伺服器的預設埠號。
Host: developer.cdn.mozilla.net
複製程式碼
If-Match
形如If-xxx這樣樣式的請求首部欄位,都是條件請求。伺服器接收到附帶條件的請求後,只有判斷指定條件為真時,才會執行請求。
If-Match,它會告知伺服器匹配資源所用的ETag值,伺服器會比對If-Match的欄位值和資源的ETag值,僅當兩者一致時,才會執行請求。ETag 之間的比較使用的是強比較演算法,即只有在每一個位元組都相同的情況下,才可以認為兩個檔案是相同的。在 ETag 前面新增 W/ 字首表示可以採用相對寬鬆的演算法。
If-None-Match,它與If-Match功能正好相反。
If-Match
If-Match: "bfc13a64729c4290ef5b2c2730249c88ca92d82d"
If-Match: W/"67ab43", "54ed21", "7892dd"
If-Match: *
複製程式碼
If-None-Match
If-None-Match: "bfc13a64729c4290ef5b2c2730249c88ca92d82d"
If-None-Match: W/"67ab43", "54ed21", "7892dd"
If-None-Match: *
複製程式碼
If-Modified-Since/If-Unmodified-Since
If-Modified-Since,伺服器只在所請求的資源在 If-Modified-Since 給定的日期時間之後對內容進行過修改的情況下才會將資源返回,狀態碼為200。如果未經修改,那麼返回一個不帶有訊息主體的 304 響應,而在 Last-Modified 首部中會帶有上次修改時間。
If-Unmodified-Since,它與If-Modified-Since作用相反
If-Modified-Since
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
複製程式碼
If-Unmodified-Since
If-Unmodified-Since: Wed, 21 Oct 2015 07:28:00 GMT
複製程式碼
If-Range
該欄位與Range欄位配合使用,If-Range欄位值中的條件得到滿足時,Range 頭欄位才會起作用,同時伺服器回覆狀態碼206(部分內容)以及返回Range欄位指定的內容。反之沒有得到滿足,伺服器則將會返回狀態碼200,並返回完整的請求資源。
If-Range: Wed, 21 Oct 2015 07:28:00 GMT
複製程式碼
Max-Forwards
前面在講到TRACE方法時,我們講到了Max-Forwards欄位。使用HTTP協議通訊時,請求可能會經過多個代理伺服器。途中,如果代理伺服器由於某些原因導致請求轉發失敗,中間我們是不知道是哪臺代理伺服器失敗了。而設定Max-Forwards欄位之後,每經過一臺代理伺服器,Max-Forwards便會減1,到0之後便不會進行轉發,而是直接返回響應。這樣我們就可以輕鬆排查出是哪臺代理伺服器出問題了。
Proxy-Authorization
這個欄位的作用跟Authorization是一樣的,他們之間的區別在於Proxy-Authorization用於客戶端與代理伺服器之間的認證,而Authorization是用於客戶端和伺服器之間的認證
Proxy-Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
複製程式碼
Range
該欄位用於只獲取部分資源的範圍請求,Range欄位指定了其範圍。成功處理範圍請求,則返回狀態碼 206 (部分內容)。若指定範圍超出邊界,則返回狀態碼416 Range Not Satisfiable (範圍無法滿足)。範圍合法但沒有成功處理,則返回狀態碼200,並響應全部資源。
Range: bytes=200-1000, 2000-6576, 19000-
複製程式碼
Referer
當前請求頁面的來源頁面的地址,即表示當前頁面是通過此來源頁面裡的連結進入的。
Referer: https://developer.mozilla.org/en-US/docs/Web/JavaScript
複製程式碼
TE
該欄位用於告知伺服器客戶端能夠處理響應的傳輸編碼方式及相對優先順序。這裡不要跟Accept-Encoding弄混, TE是用於傳輸編碼,而Accept-Encoding用於內容編碼。
TE: trailers, deflate;q=0.5
複製程式碼
User-Agent
發起請求的瀏覽器或者使用者代理軟體的應用型別、作業系統、軟體開發商以及版本號等。
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Name
複製程式碼
響應首部欄位
Accept-Ranges
用於告知客戶端伺服器是否能處理範圍請求,若能,則定義範圍請求的單位。取值有兩種none和bytes。
Accept-Ranges: bytes //範圍請求單位為bytes
Accept-Ranges: none //不支援範圍請求
複製程式碼
Age
當快取伺服器用自己的快取的資源去響應請求時,用該頭部標識該資源在快取伺服器快取的時長,單位為秒。
Age:600
複製程式碼
ETag
伺服器分配給每份資源的唯一識別符號。當資源更新時,ETag也會相應更新。ETag分為強ETag和弱ETag。
Etag:"33a64df551425fcc55e4d42a148795d9f25f89d4" //無論實體發生多麼細微的變化都會改變其值。
ETag: W/"0815" //只有資源發生了根本改變,產生差異時才改變ETag值。欄位值最開始處附加W/
複製程式碼
Location
指定需要將頁面重新定向至的地址,一般在響應碼為3xx的響應中才會有意義。
Location:http//www.haimaiche.com/index.html
複製程式碼
Retry-After
告知客戶端應該在多久之後再次傳送請求。主要配合503 Service Unavailable 或者3XX Redirect響應一起使用。取值可以是具體的日期時間也可以是建立響應後的秒數。
Retry-After: Wed, 21 Oct 2015 07:28:00 GMT
Retry-After: 120
複製程式碼
Server
包含伺服器所用到的軟體相關資訊。
Server: Apache/2.4.1 (Unix)
複製程式碼
Vary
用於控制快取。從代理伺服器接收到伺服器返回包含Vary指定項的響應之後,若再要進行快取,僅對請求中含有相同Vary指定首部欄位的請求返回快取。即使對相同資源發起請求,但由於Vary指定的首部欄位不相同,也必須要從源伺服器重新獲取資源。
Vary:Accept-Language
複製程式碼
註解: 當代理伺服器接收到帶有Vary首部欄位指定獲取資源的請求時,如果使用的Accept-Language欄位的值相同時,則直接從快取返回響應。反之,則需要先從源伺服器端獲取資源後才能作為響應返回。
WWW-Authenticate
定義了使用何種驗證方式去獲取對資源的連線。
WWW-Authenticate: Basic realm="Access to the staging site"
複製程式碼
通用首部欄位
Cache-Control
指定指令,用於控制快取行為。指令可以多選,中間用”,“分割。
Cache-Control: no-cache, no-store, must-revalidate
複製程式碼
快取請求指令
指令 | 引數 | 說明 |
---|---|---|
no-cache | 無 | 強制向源伺服器再次驗證 |
no-store | 無 | 不快取請求或響應的任何內容 |
no-transform | 無 | 代理不可更改媒體型別 |
only-if-cached | 無 | 從快取獲取資源 |
max-age= | 必需 | 響應的最大Age值 |
max-stale[=] | 可省略 | 接受已過期的響應 |
min-fresh= | 必需 | 期望在指定時間內的響應仍有效 |
快取響應指令
指令 | 引數 | 說明 |
---|---|---|
no-cache | 無 | 快取前必須先確認其有效性 |
no-store | 無 | 不快取請求或響應的任何內容 |
no-transform | 無 | 代理不可更改媒體型別 |
public | 無 | 可向任意方提供響應的快取 |
private | 無 | 僅向特定使用者返回響應 |
must-revalidate | 無 | 可快取但必須再向源伺服器進行確認 |
proxy-revalidate | 無 | 要求中間快取伺服器對快取的響應有效性再進行確認 |
max-age= | 必需 | 響應的最大Age值 |
s-maxage= | 必需 | 公共快取伺服器響應的最大Age值 |
Connection
決定當前的事務完成後,是否會關閉網路連線。Http協議1.1之後預設都是keep-alive(持久連線),1.0則是close(非持久連線)。
Connection: keep-alive
Connection: close
複製程式碼
Date
標明HTTP報文的建立日期和時間。
Date: Wed, 21 Oct 2015 07:28:00 GMT
複製程式碼
Pragma
用來向後相容只支援 HTTP/1.0 協議的快取伺服器
Pragma: no-cache //唯一形式
複製程式碼
Trailer
允許傳送方在分塊傳送的訊息後面新增額外的元資訊,常用於分塊傳輸編碼中。
HTTP/1.1 200 OK
Content-Type: text/plain
...
Transfer-Encoding: chunked
Trailer: Expires
...(報文主體)...
0
Expires: Wed, 21 Oct 2015 07:28:00 GMT
複製程式碼
以上用例中,指定首部欄位Trailer的值為Expires,在報文主體之後(分塊長度0之後)出現首部欄位Expires。
Transfer-Encoding
規定了報文主體時採用的編碼方式。
Transfer-Encoding: chunked
複製程式碼
Via
用於追蹤客戶端與伺服器之間的請求和響應報文的傳輸路徑。報文經過代理或閘道器時,會先在首部欄位Via中附加該伺服器的資訊,然後再進行轉發。經常和TRACE方法一起使用。
Via: 1.0 fred, 1.1 p.example.net
複製程式碼
Warning
用於告知使用者一些與快取相關的問題的警告。
Warning: <警告碼> <警告的主機:埠號> <警告為別把> [<日期時間(可選)>]
Warning: 112 gw.hacker.jp:8080 "cache down" " Wed, 21 Oct 2015 07:28:00 GMT"
複製程式碼
警告碼錶
警告碼 | 警告內容 | 說明 |
---|---|---|
110 | Response is Stale | 由快取伺服器提供的響應已過期(設定的失效時間已過)。 |
111 | Revalidation Failed | 由於無法訪問伺服器,響應驗證失敗。 |
112 | Disconnected Operation | 快取伺服器斷開連線。 |
113 | Heuristic Expiration | 如果快取伺服器採用啟發式方法,將快取的有效時間設定為24小時,而在該響應的年齡超過24小時時傳送。 |
199 | Miscellaneous Warning | 任意的警告資訊。 |
214 | Transformation Applied | 由代理伺服器新增,如果它對返回的展現內容進行了任何轉換,比如改變了內容編碼、媒體型別等。 |
299 | Miscellaneous Warning | 由於無法訪問伺服器,響應驗證失敗。 |
實體首部欄位
Allow
告訴客戶端資源所支援的HTTP方法。
Allow: GET, POST, HEAD
複製程式碼
Content-Encoding
告知客戶端伺服器對實體主體部分選用的內容編碼方式。
Content-Encoding: gzip
複製程式碼
Content-Language
告知客戶端,實體主體使用的自然語言。
Content-Language: zh-CN
複製程式碼
Content-Length
表明實體主體部分的大小,單位是位元組。
Content-Length:15000
複製程式碼
Content-Location
表示返回的資料對應的URI,主要用於指定要訪問的資源經過內容協商後的結果的URI。
Content-Location:http://www.hacker.jp/index.html
複製程式碼
Content-Range
主要用於範圍請求,告知客戶端當前傳送部分的內容範圍以及整個實體大小。
Content-Range: bytes 200-1000/67589
複製程式碼
Content-Type
表明實體主體內物件的媒體型別。
Content-Type: text/html; charset=utf-8
複製程式碼
Expires
將資源的失效日期告訴客戶端。快取伺服器在接收到含有首部欄位Expires的響應後,在Expires指定時間之前,響應的副本會一直被儲存。反之,快取伺服器會向源伺服器請求資源。當Cache-Control有指定max-age或者s-maxage指令時,Expires則會被忽略。
Expires: Thu, 01 Dec 1994 16:00:00 GMT
複製程式碼
Last-Modified
表明資源最終的修改時間。
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
複製程式碼
其他擴充首部欄位
Set-Cookie
用來由伺服器端向客戶端傳送cookie
Set-Cookie: id=a3fWa;Domain=somecompany.co.uk; Path=/ Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
複製程式碼
- Domain: 指定Cookie可以傳送的主機名
- Path: 限定可以傳送Cookie的路徑
- Expires: 指定Cookie的有效期
- Max-Age: 多少秒之後Cookie失效,ie8及其以下不支援這個屬性,Max-Age優先順序大於Expires
- Secure: 僅在HTTPS安全通訊時才會傳送Cookie
- HttpOnly: 使js無法獲取Cookie
Cookie
含有先前由伺服器通過Set-Cookie首部投放並儲存到客戶端的Cookie。
Cookie: PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;
複製程式碼
DNT
位於HTTP請求首部。全稱Do Not Track。表示拒絕被精準廣告追蹤的一種方法。
DNT:0;//同意目標站點追蹤使用者個人資訊。
DNT:1;//不同意目標站點追蹤使用者個人資訊。
複製程式碼
X-Frame-Options
主要位於HTTP響應首部,用於控制網站內容在其他Web網站的Frame標籤內的顯示問題。 其主要目的是為了防止點選劫持(clickjacking)攻擊。
有以下三個取值:
- DENY:表示該頁面不允許在 frame 中展示,即便是在相同域名的頁面中巢狀也不允許。
- SAMEORIGIN:表示該頁面可以在相同域名頁面的 frame 中展示。
- ALLOW-FROM uri:表示該頁面可以在指定來源的 frame 中展示。
X-Frame-Options: DENY
X-Frame-Options: SAMEORIGIN
X-Frame-Options: ALLOW-FROM http://caibaojian.com/
複製程式碼
X-XSS-Protection
當檢測到跨站指令碼攻擊 (XSS)時,瀏覽器將停止載入頁面。針對現代瀏覽器,會選擇更強大的Content-Security-Policy。關於Content-Security-Policy可參考阮一峰老師的這篇文章《Content Security Policy 入門教程》
X-XSS-Protection: 0 //禁止XSS過濾。
X-XSS-Protection: 1 //啟用XSS過濾(通常瀏覽器是預設的)。如果檢測到跨站指令碼攻擊,瀏覽器將清除頁面(刪除不安全的部分)。
X-XSS-Protection: 1; mode=block //啟用XSS過濾。 如果檢測到攻擊,瀏覽器將不會清除頁面,而是阻止頁面載入。
X-XSS-Protection: 1; report=<reporting-uri> //啟用XSS過濾。 如果檢測到跨站指令碼攻擊,瀏覽器將清除頁面並使用CSP report-uri指令的功能傳送違規報告。
複製程式碼
確認Web安全的HTTPS
說到HTTPS,相信大家都很熟悉,什麼是HTTPS?說白了就是HTTP的安全版,在HTTP層面上加入了安全控制。下面我們要說說HTTP有哪些安全問題?HTTPS又是如何保證安全的。
HTTP缺點
通訊使用明文可能會被竊聽
由於HTTP本身不具備加密的功能,所以HTTP報文都是以明文的方式傳送的。而整個網際網路那些網路裝置都不可能是你個人的,這就不能排除某個環節中遭到惡意窺探的行為。
即使你人為地在傳送之前的採用對稱加密演算法加密了,且不說影響效率,接收方每次都需要解密一下。這樣可以真的保證其安全嗎?答案是不能保證的。接收方想要解密密文首先得知道這採用什麼方式加密的或者金鑰又是啥?這個是需要傳送方給接收方的,而這個傳送金鑰或者加密方式的過程依舊是處於被竊聽的危險中,所以僅僅靠加密資料是無法保證其安全性的。
不驗證通訊方的身份就有可能遭遇偽裝
HTTP協議是不會對通訊方進行確認的。無論誰傳送過來的請求都會返回響應。這就可能存在有偽裝的客戶端或者伺服器,一些本有許可權控制的資源遭到盜取。Dos攻擊就是利用HTTP協議不進行通訊方確認的漏洞,傳送海量無意義請求,超出伺服器的負荷,導致伺服器奔潰當機。
無法證明報文完整性,可能已遭篡改
前面我們說到網際網路上網路裝置由於不屬於你個人的,很難保證他人會不會劫取你的資訊並進行篡改。導致伺服器傳送給客戶端的檔案和客戶端實際接收到的檔案不能保證一致。
HTTP通訊過程
第一步: 客戶端傳送一段包含有客戶端支援的SSL/TLS協議版本、可支援加密元件(使用的加密演算法以及金鑰長度等)報文給伺服器。 第二步: 伺服器會根據客戶端支援的SSL/TLS協議版本以及其支援的加密中選擇一種,作為後續通訊過程中所使用的SSL/TLS協議以及加密元件,並放在報文中告知客戶端。 第三步: 之後伺服器會再傳送一段報文,其中包含公開金鑰證照。與公鑰相配對的私鑰則留在伺服器自己那。 第四步: 客戶端拿到公開金鑰的證照之後,會向頒發數字認證證照的機構確認其合法性。若合法,則會根據與伺服器協商確定的加密元件,隨機生成一個金鑰,而這個金鑰將用於後續報文的加密。並將生成好的金鑰用伺服器給的公鑰進行加密,併傳送給伺服器。 第五步: 伺服器拿到客戶端加密過得金鑰之後,會用之前留在自己那的私鑰去解密獲取到金鑰。 第五步: 客戶端與伺服器之間通訊的報文都會用到這個金鑰進行加密。
上面的過程第一次讀可能無法理解,現在在重新總結一下。整個HTTPS通訊大體可以分為三個階段:
1.協商決定加密元件 2.確定加密解密金鑰 3.客戶端與伺服器開始報文通訊
這裡我們理清楚一點,最終用來加密解密的金鑰並不是由伺服器生成的,而是客戶端生成的。其中部分細節我們在後續會講到。
HTTP+加密+認證+完整性保護=HTTPS
首先我們弄要弄清楚一件事,HTTPS並非應用層的一種新協議。只是HTTP通訊介面部分用SSL或者TLS協議代替而已。原先HTTP協議直接和TCP協議對接,而HTTPS則是HTTP先與SSL/TLS通訊,再由SSL/TLS和TCP通訊。
加密
前面我們說到HTTP是採用明文傳輸的,這就免不了你的資訊會被他人窺探。即使是採用對稱加密演算法,無法保證你在傳送金鑰的過程中不被他人竊取到。換句話說我們保證了金鑰的安全傳輸,那麼整個通訊過程就不怕被窺探到了。HTTPS採用了非對稱加密的方式,用公鑰去加密,用私鑰去解密。伺服器把公鑰交給客戶端去加密通訊金鑰,然後再用自己的私鑰去解密就可獲取到通訊金鑰。常見的非對稱加密演算法就是RSA,有興趣的小夥們可以自行了解一下,這裡不再贅述。
上面提到了非對稱演算法感覺是個好東西,伺服器和客戶端通訊的報文采用非對稱加密演算法不就完了,幹嘛還要繞一個彎,採用非對稱加密+對稱加密的組合。HTTPS這樣設計是有其原因的,非對稱加密演算法看似很美好,但是其對CPU和記憶體的開銷太大。有人做過實驗,在加解密同等數量的檔案下,非對稱演算法的開銷是對稱演算法的1000倍以上。由此可見,這個非對稱演算法是多影響效能。效能這個問題,就算可以忍受,非對稱演算法有一個致命的缺點就是加密內容的長度不得超過公鑰長度,常用的公鑰長度是2048位,也就是256個位元組,也就意味著加密的密文的大小不能超過256個位元組。這個就太坑爹了,現在網路上的圖片隨隨便便就是幾千位元組,這個就真的不能忍了。如果這樣你都能忍,那你是真的diao,是在下輸了。
認證
前面說到HTTP整個過程中,你的報文是可以被劫取到的,所以在傳送公鑰的過程中很難保證其不被掉包。為了保證金鑰的合法性,HTTPS採用了數字證照這個概念。整個數字證照認證流程是這樣子的:
首先伺服器的運營人員會想權威的第三方數字證照認證機構申請公開金鑰。數字證照認證機構在判明申請者的身份之後,對已申請的公開金鑰做數字簽名的操作,並把這個數字簽名分配給這個公開金鑰,並將已簽名過的公開金鑰放在公鑰證照裡面。客戶端拿到這個公鑰證照之後,向數字證照認證機構提出驗證公鑰證照上數字簽名的請求,以確認公開金鑰的真實性。
有了第三方的數字證照驗證機構,我們就可以保證了公開金鑰不被他人惡意篡改。
完整性保護
有了加密和認證足以保證通訊報文可以不被他人看到,但我們報文是可以被他人擷取到進行篡改的。比如完整的資訊是這樣子的“我不要你做我的女朋友了,我要你做我的老婆”,這本來是一句非常浪漫的話,要是被破壞了只傳了“我不要你做我的女朋友了”,接收方還不知道這個資訊是被篡改過得,那麼就出大事了,這對小情侶就拜拜了,一段好姻緣就這麼被破壞了。為了世界和平,我們還保護報文的完整性。HTTPS使用了MAC演算法來保證其完整性。傳送方傳送報文會帶有一個由MAC演算法得出的一個MAC值。接受方拿到報文之後會根據金鑰和MAC演算法再算出一個MAC值,與傳過來的MAC值進行比對,若一致則沒有遭受篡改,這樣就可以知道報文有沒有被篡改過。
HTTPS相比較HTTP的缺點
HTTPS相比較HTTP並不是各個方面都強於HTTP的。若是這樣,那我們現在怕是見不到採用HTTP協議的網站了。HTTPS相比較HTTP主要有三個缺點:
速度慢
原本HTTP直接和TCP進行通訊,現在中間出現了一個第三者SSL/TLS,勢必會造成處理的通訊量變大,拖累速度。
CPU及記憶體等資源的消耗大
頻繁地加解密,毫無疑問就需要更多CPU和記憶體等資源的支援,導致負載增強。
要錢
要進行HTTPS通訊,證照是必不可少的,這個得向認證機構購買的,人認證機構不可能白給你的,收取點費用也是理所當然的。前面兩個為了安全還是可以接受的,這個原因我個人覺得是一些網站不使用HTTPS的主要原因,畢竟談錢傷感情嘛。當然窮逼不代表沒有活路,總有一些大神在默默地拯救著我們這群窮屌絲,[《Let's Encrypt,免費好用的 HTTPS 證照》][15] 。另外再幫別人打個小廣告,[《給網站戴上「安全套」》][16],請記住我的名字,我叫雷鋒!
確認訪問使用者身份的認證
隨著Web技術的發展,越來越多的網站都更加地精細化,有些內容或者操作只有特定的使用者才能看見或者操作。此時,就需要驗證坐在計算機前面的那個人是不是屬於那些特定使用者,下面我們就說說HTTP使用的認證方式。
BASIC認證
流程1: 當客戶端訪問的資源需要BASIC認證時,伺服器會返回401,並在響應頭新增首部欄位WWW-Authenticate,該欄位包含驗證方式(BASIC)以及realm(告訴客戶端要訪問的資源屬於伺服器眾多區域中的哪一片區域裡, 如果未指定realm, 客戶端通常顯示一個格式化的主機名來替代。) 流程2: 客戶端接收到狀態碼401之後,就會根據響應頭中的WWW-Authenticate指定的認證方式(BASIC)把使用者名稱和密碼用冒號(:)進行連線,然後再Base64編碼處理,最後塞到請求頭首部欄位Authorization中傳送給伺服器。
舉個例子:
使用者名稱:admin 密碼:123456
admin:123456 => YWRtaW46MTIzNDU2
Authorization: BASIC YWRtaW46MTIzNDU2
複製程式碼
流程3: 伺服器接收到含有首部欄位Authorization的請求之後,對其認證資訊進行確認。通過,則返回其請求的資源。
BASIC認證僅僅做了Base64編碼,並未進行進行加密處理,很有可能會被他人竊聽盜取,安全性太差。
DIGEST認證
流程1: 當客戶端訪問的資源需要DIGEST認證時,伺服器會返回401,響應頭部比Basic模式複雜,WWW-Authenticate: Digest realm=”myTomcat”,qop="auth",nonce="xxxxxxxxxxx",opaque="xxxxxxxx" 。其中qop的auth表示鑑別方式;nonce是隨機字串;opaque服務端指定的值,客戶端需要原值返回。 流程2: 瀏覽器彈出對話方塊讓使用者輸入使用者名稱和密碼,瀏覽器對使用者名稱、密碼、nonce值、HTTP請求方法、被請求資源URI等組合後進行MD5運算,把計算得到的摘要資訊傳送給服務端。請求頭部類似如下,Authorization: Digest username="xxxxx",realm="myTomcat",qop="auth",nonce="xxxxx",uri="xxxx",cnonce="xxxxxx",nc=00000001,response="xxxxxxxxx",opaque="xxxxxxxxx" 。其中username是使用者名稱;cnonce是客戶端生成的隨機字串;nc是執行認證的次數;response就是最終計算得到的摘要。 **流程3:**服務端web容器獲取HTTP報文頭部相關認證資訊,從中獲取到username,根據username獲取對應的密碼,同樣對使用者名稱、密碼、nonce值、HTTP請求方法、被請求資源URI等組合進行MD5運算,計算結果和response進行比較,如果一致則認證成功並返回相關資源。
DIGEST認證相比BASIC認證沒有將密碼傳輸出去,在安全性上要比BASIC認證提高很多。但是要是攻擊者擷取你的報文便可以用報文首部欄位Authorization的值偽裝起來向伺服器發起請求。
SSL客戶端驗證
前面我們說到HTTPS的時候,是伺服器作為傳送證照的一方,而客戶端作為接受證照驗證證照的一方。這裡,正好兩者角色對調了一下,通話金鑰的生成方在伺服器端,而不是客戶端。一般不會獨立使用,而是和表單驗證配合使用。SSL客戶端驗證用來驗證客戶端計算機,而表單驗證則用來驗證坐在計算機前的人。
基於表單驗證
目前主流的網站都是用的是表單驗證的方式。它的原理很簡單,客戶端提交帶有使用者名稱和密碼的表單交給後臺,後臺會生成一個特定的SessionId放在響應頭欄位Set-Cookie中,客戶端接受到之後儲存到本地Cookie。之後的請求,都會把Cookie帶上,伺服器便會根據Cookie裡面存的SessionId去識別使用者。
總結
這篇文章是在閱讀完《圖解HTTP協議》這本書之後作的的一篇總結性文章。HTTP協議我們重點要掌握的是TCP/IP模型、HTTP的通訊過程、HTTP狀態碼、HTTP首部、HTTPS的原理、HTTP和HTTPS的各自優勢。其中HTTP狀態碼和HTTP首部是本文的重中之重,文章花了大量篇幅去講解這兩塊。還有HTTPS,現在越來越多的網站開始採用這個協議,所以掌握HTTPS必不可少。希望這篇文章可以幫助那些對HTTP協議還很模糊的小夥伴們可以加深對HTTP協議的理解,本文不足之處歡迎留言指出。最後寫部落格不易,還望小夥伴們多多點贊收藏支援!