目錄
一、HTTP 請求內容
由於最新的http2,並沒有被各大瀏覽器廣泛使用,所以本文是基於http/1.1所編寫的。
同時經過檢測我們也發現,chrome等瀏覽器也正是使用http/1.1版本的。
關於http/1.1協議的詳情,可查看官方文檔
我們打開chrome的network,點擊任何一條request請求,即可發現,每個http headers都包含以下部分:Genaral,Request Headers,Response Headers,Request Payload。
General(不屬於headers,只用於收集請求url和響應的status等信息)
Request Headers(請求headers)
Response Headers(響應headers)
Request Payload(請求參數)
二、HTTP Headers分類
在http heanders中,爲了方便,分爲以下幾類:Genaral headers(和上面說的General不同,這個只是爲了方便統計),Request Headers,Response Headers,Entity Headers(也是爲了方便統計)。
所以,一個完整的請求頭/響應頭,應該除了自身,還包括 General Headers和Entity Headers。
1、Genaral headers: 同時適用於請求和響應消息,但與最終消息傳輸的數據無關的消息頭。
2、Request Headers: 包含更多有關要獲取的資源或客戶端本身信息的消息頭。
3、Response Headers:包含有關響應的補充信息,如其位置或服務器本身(名稱和版本等)的消息頭。
4、Entity Headers:包含有關實體主體的更多信息,比如主體長(Content-Length)度或其MIME類型。
1、Genaral headers
Cache-Control——控制緩存的行爲; 詳情
Connection——決定當前的事務完成後,是否會關閉網絡連接; 詳情
Date——創建報文的日期時間; 詳情
Keep-Alive——用來設置超時時長和最大請求數;詳情
Via——代理服務器的相關信息;詳情
Warning——錯誤通知;詳情
Trailer——允許發送方在分塊發送的消息後面添加額外的元信息; 詳情
Transfer-Encoding——指定報文主體的傳輸編碼方式;詳情
Upgrade——升級爲其他協議;
2、Request headers
Accept——客戶端可以處理的內容類型;詳情
Accept-Charset——客戶端可以處理的字符集類型;詳情
Accept-Encoding——客戶端能夠理解的內容編碼方式;詳情
Accept-Language——客戶端可以理解的自然語言;詳情
Authorization——Web 認證信息;詳情
Cookie——通過Set-Cookie設置的值;詳情
DNT——表明用戶對於網站追蹤的偏好;詳情
From——用戶的電子郵箱地址;詳情
Host——請求資源所在服務器;詳情
If-Match——比較實體標記(ETag);詳情
If-Modified-Since——比較資源的更新時間;詳情
If-None-Match——比較實體標記(與 If-Match 相反);詳情
If-Range——資源未更新時發送實體 Byte 的範圍請求;詳情
If-Unmodified-Since——比較資源的更新時間(與 If-Modified-Since 相反);詳情
Origin——表明了請求來自於哪個站點;詳情
Proxy-Authorization——代理服務器要求客戶端的認證信息;詳情
Range——實體的字節範圍請求;詳情
Referer——對請求中 URI 的原始獲取方;詳情
TE——指定用戶代理希望使用的傳輸編碼類型;詳情
Upgrade-Insecure-Requests——表示客戶端優先選擇加密及帶有身份驗證的響應;詳情
User-Agent——瀏覽器信息;詳情
3、Response Headers
Accept-Ranges——是否接受字節範圍請求;詳情
Age——消息對象在緩存代理中存貯的時長,以秒爲單位;詳情
Clear-Site-Data——表示清除當前請求網站有關的瀏覽器數據(cookie,存儲,緩存);詳情
Content-Security-Policy——允許站點管理者在指定的頁面控制用戶代理的資源;詳情
Content-Security-Policy-Report-Only—— 詳情
ETag——資源的匹配信息;鏈接描述
Location——令客戶端重定向至指定 URI;詳情
Proxy-Authenticate——代理服務器對客戶端的認證信息;詳情
Public-Key-Pins——包含該Web 服務器用來進行加密的 public key (公鑰)信息;詳情
Public-Key-Pins-Report-Only——設置在公鑰固定不匹配時,發送錯誤信息到report-uri;詳情
Referrer-Policy——用來監管哪些訪問來源信息——會在 Referer 中發送;詳情
Server——HTTP 服務器的安裝信息;詳情
Set-Cookie——服務器端向客戶端發送 cookie;詳情
Strict-Transport-Security——它告訴瀏覽器只能通過HTTPS訪問當前資源;詳情
Timing-Allow-Origin——用於指定特定站點,以允許其訪問Resource Timing API提供的相關信息;詳情
Tk——顯示了對相應請求的跟蹤情況;詳情
Vary——服務器緩存的管理信息;詳情
WWW-Authenticate——定義了使用何種驗證方式去獲取對資源的連接;詳情
X-XSS-Protection——當檢測到跨站腳本攻擊 (XSS)時,瀏覽器將停止加載頁面;詳情
4、Entity Headers
Allow——客戶端可以處理的內容類型,這種內容類型用MIME類型來表示;詳情
Content-Encoding——用於對特定媒體類型的數據進行壓縮;詳情
Content-Language——訪問者希望採用的語言或語言組合;詳情
Content-Length——發送給接收方的消息主體的大小;詳情
Content-Location——替代對應資源的 URI;詳情
Content-Range——實體主體的位置範圍;詳情
Content-Type——告訴客戶端實際返回的內容的內容類型;詳情
Expires——包含日期/時間, 即在此時候之後,響應過期;詳情
Last-Modified——資源的最後修改日期時間;詳情
三、HTTP 具體應用
1、Cookie
HTTP 協議是無狀態的,主要是爲了讓 HTTP 協議儘可能簡單,使得它能夠處理大量事務。HTTP/1.1 引入 Cookie 來保存狀態信息。
Cookie 是服務器發送到用戶瀏覽器並保存在本地的一小塊數據,它會在瀏覽器之後向同一服務器再次發起請求時自動被攜帶上,用於告知服務端兩個請求是否來自同一瀏覽器。由於之後每次請求都會需要攜帶 Cookie 數據,因此會帶來額外的性能開銷(尤其是在移動環境下)。
Cookie 曾一度用於客戶端數據的存儲,因爲當時並沒有其它合適的存儲辦法而作爲唯一的存儲手段,但現在隨着現代瀏覽器開始支持各種各樣的存儲方式,Cookie 漸漸被淘汰。新的瀏覽器 API 已經允許開發者直接將數據存儲到本地,如使用 Web storage API(本地存儲和會話存儲)或 IndexedDB。
a、創建過程
服務器發送的響應報文包含Set-Cookie首部字段,客戶端得到響應報文後把 Cookie 內容保存到瀏覽器中。
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: PHPSESSID=kq8v6iujarsgflkeq7shmai9c7
客戶端之後對同一個服務器發送請求時,會從瀏覽器中取出 Cookie 信息並通過 Cookie 請求首部字段發送給服務器。
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: PHPSESSID=kq8v6iujarsgflkeq7shmai9c7
b、分類
會話期 Cookie:瀏覽器關閉之後它會被自動刪除,也就是說它僅在會話期內有效。
持久性 Cookie:指定一個特定的過期時間(Expires)或有效期(max-age)之後就成爲了持久性的 Cookie。
安全 Cookie:指定HttpOnly,這樣cookie不能使用 JavaScript 經由 Document.cookie 屬性,來防範跨站腳本攻擊(XSS)。
HTTPS Cookie: 指定Secure,只有在請求使用SSL和HTTPS協議的時候纔會被髮送到服務器。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;HttpOnly
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;Secure
2、緩存
降低客戶端獲取資源的延遲:緩存通常位於內存中,讀取緩存的速度更快。並且緩存在地理位置上也有可能比源服務器來得近,例如瀏覽器緩存(但是隻能緩存get,不能緩存其他類型請求)。
cache FAQ MDN
流程圖:
a、判斷cache-control或者expires是否有效
max-age值爲緩存的毫秒數。
可以看到response-headers中設置了cache-control,並大於0,則下次直接從緩存(from disk cache)中獲取
b、Etag判斷
當發現Cache-Control設置的毫秒數過期了,則直接發送請求:
——如果響應中包含Etag(服務器生成的值),則瀏覽器再次向web服務器發送請求並帶上頭If-None-Match(Etag的值),web服務器收到請求後發現有頭If-None-Match 則與被請求資源的相應校驗串進行比對,決定返回200或304(瀏覽器會從緩存中獲取)。
——如果響應中不包含Etag,則進行Last-Modified判斷
c、Last-Modified判斷
當發現response header中含有Last-Modified,則再次向web服務器請求時帶上頭 If-Modified-Since,web服務器收到請求後發現有頭If-Modified-Since 則與被請求資源的最後修改時間進行比對,然後服務器決定返回200或者304(緩存)
瀏覽器強制告訴服務器不緩存資源:
//request headers
Cache-Control:max-age=0, no-cache
四、HTTP 請求 method
五、HTTP 狀態碼
1、1XX
- 100(continue) 表明到目前爲止都很正常,客戶端可以繼續發送請求或者忽略這個響應。
2、2XX
- 200(OK) 表示從客戶端發來的請求在服務器端被正常處理了。
- 204(No Content) 該狀態碼代表服務器接收的請求已成功處理,但在返回的響應報文中不含實體的主體部分。
- 206(Partial Content) 該狀態碼錶示客戶端進行了範圍請求,而服務器成功執行了這部分的 GET 請求。響應報文中包含由 Content-Range 指定範圍的實體內容。
3、3XX
- 301(Moved Permanently) 永久性重定向。該狀態碼錶示請求的資源已被分配了新的 URI,以後應使用資源現在所指的 URI。
- 302(Found) 臨時性重定向。比如在沒有登錄情況下訪問網站"個人中心",會重定向到登錄頁,但是你登錄後,訪問個人中心時,它又不會重定向到其他地方了。
- 303(See Other) 和 302 有着相同的功能,但是 303 明確要求客戶端應該採用 GET 方法獲取資源。
- 304(Not Modified) 如果請求報文首部包含一些條件,例如:If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,如果不滿足條件,則服務器會返回 304 狀態碼。
4、4XX
- 400(Bad Request) 該狀態碼錶示請求報文中存在語法錯誤。當錯誤發生時,需修改請求的內容後再次發送請求。
- 401 Unauthorized 該狀態碼錶示發送的請求需要有認證信息。返回含有 401 的響應必須包含一個適用於被請求資源的 WWW-Authenticate 首部用以詢問用戶信息。當瀏覽器初次接收到 401 響應,會彈出認證用的對話窗口。第二次接收到,則不彈出,直接表示認證失敗。
- 403(Forbidden) 對請求資源的訪問被服務器拒絕了,一般是未獲得文件系統的訪問授權,問權限出現某些問題。
- 404(Not Found) 瀏覽器地址錯誤。服務器找不到對應資源。
5、5XX
- 500(Internal Server Error) 服務器在執行時報錯。
- 503(Service Unavailable) 服務器暫時處於超負載或正在進行停機維護,無響應。一般需要重啓服務器即可。
六、MIME類型
瀏覽器通常使用MIME類型(而不是文件擴展名)來確定如何處理文檔;因此設置正確的MIME類型附加到headers是非常重要的。
除了上面的基本的5中類型外,還有一種類型,即multipart類型。
multipart/form-data 在表單中通過post上傳
multipart/byteranges (不常用,不知道)
一般的,如果沒有顯示的在request headers的Allow上設置類型,瀏覽器的MIME 嗅探會推測出來對應的類型,如果發現找不到特定的子類型,則給出默認類型,比如對於text文件類型若沒有特定的subtype,就使用 text/plain。類似的,二進制文件沒有特定或已知的 subtype,即使用 application/octet-stream。
七、HTTP 使用的認證方式
1、BASIC 認證(基本認證)
2、DIGEST 認證(摘要認證)
3、SSL 客戶端認證
4、FormBase 認證(基於表單認證)
1、BASIC 認證
當在瀏覽器端輸入一個url時,會自動彈出一個框,要求輸入用戶名和密碼。此種方式爲basic認證。
下面是認證執行過程:
第一步:在瀏覽器地址欄中輸入 http://localhost:8080/auth
第二步: 服務器執行,發現需要認證,返回這個請求的響應。並在response headers中添加WWW-Authenticate,將http請求狀態設置爲401.
瀏覽器檢測到WWW-Authenticate爲basic後,自動彈出框。
第三步: 當用戶看到框後,輸入 用戶名和密碼,瀏覽器會將用戶名和密碼通過base64方式編碼,然後添加到 request headers的 Authorization 中發送給服務器,瀏覽器驗證通過,返回200狀態碼
如果驗證不通過,則繼續返回狀態碼401,提示驗證失敗。
缺點:
BASIC 認證雖然採用 Base64 編碼方式,但這不是加密處理。不需要任何附加信息即可對其解碼。換言之,由於明文解碼後就是用戶 ID和密碼,在 HTTP 等非加密通信的線路上進行 BASIC 認證的過程中,如果被人竊聽,被盜的可能性極高。
2、DIGEST 認證
爲了彌補Basic認證沒有加密所帶來的不安全性,出現了DIGEST 認證。
過程如下:
第一步:在瀏覽器地址欄中輸入 http://localhost:8080/auth
第二步: 服務器執行,發現需要認證,返回這個請求的響應。並在response headers中添加WWW-Authenticate(包含有隨機碼nonce),將http請求狀態設置爲401.
瀏覽器檢測到WWW-Authenticate爲 digest 後,自動彈出框。
第三步: 當用戶看到框後,輸入 用戶名和密碼,瀏覽器會將用戶名和上步返回的nouce,添加到 request headers的 Authorization 中,同時也將經過 MD5 運算後的密碼字符串,生成key爲response,一併添加到Authorization 中。至此請求的request headers的Authorization 包含有如下信息。
//request header
Authorization: Digest username="my name",realm="DIGEST",nounce="xxxxxxxxxxx",algorithm="MD5",response="xxxxxxxxxxxxx"
然後發送給服務器,瀏覽器驗證通過,返回200狀態碼。
如果驗證不通過,則繼續返回狀態碼401,提示驗證失敗。
缺點: 雖然 DIGEST 認證提供了高於 BASIC 認證的安全等級, DIGEST 認證提供防止密碼被竊聽的保護機制,但並不存在防止用戶僞裝的保護機制,仍達不到多數 Web 網站對高度安全等級的追求標準。
3、SSL 客戶端認證
對於 BASIC 認證和 DIGEST 認證來說,只要輸入的用戶名和密碼正確,即可認證是本人的行爲。但如果用戶名和密碼被盜,就很有可能被第三者冒充。
而利用 SSL客戶端認證則可以避免該情況的發生。在SSL認證時,必須使用https協議。
由於SSL中的各種加密和祕鑰算法過於複雜,有興趣的可以直接閱讀SSL相關書籍,本文忽略詳細過程。
4、FormBase 認證
基於表單認證的標準規範尚未有定論,一般會使用 Cookie 來管理。
認證過程:
第一步:當用戶在瀏覽器的登錄頁面,輸入用戶名和密碼,通過http請求發送給後端。
第二步:後端保存用戶的信息到session中,並返回sessionId, 通過http添加到response headers的Set-cookie中
//response headers
Set-cookie: PHPSESSID=kq8v6iujarsgflkeq7shmai9c7
然後瀏覽器成功登錄,並跳轉頁面。
第三步:當用戶訪問個人中心或者其他頁面時。http請求的request header中會自動攜帶cookie
//request headers
Cookie: PHPSESSID=kq8v6iujarsgflkeq7shmai9c7
這樣,服務端會認爲是你本人在操作。
但是如果攻擊者通過“跨站腳本攻擊(XSS)”,通過docuemnt.cookie來獲取cookie,則sessionID很容易被盜。
爲減輕XSS造成的損失,可以事先在 Set-cookie內加上 httponly 屬性,這樣就禁止了docuemnt.cookie操作。
Set-cookie: PHPSESSID=kq8v6iujarsgflkeq7shmai9c7, httponly
八、跨域資源共享(CORS)
是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器 讓運行在一個 origin (domain) 上的Web應用被准許訪問來自不同源服務器上的指定的資源。當一個資源從與該資源本身所在的服務器不同的域、協議或端口請求一個資源時,資源會發起一個跨域 HTTP 請求。
1、簡單請求(不會觸發CORS預檢請求)
需要滿足下列所有條件:
第一條: 請求方式必須爲 GET | HEAD | POST
第二條: Content-Type 的值必須屬於下列之一:application/x-www-form-urlencoded | multipart/form-data | text/plain
第三條: 在請求中,不會發送自定義的頭部(如X-Modified)
例如: 在http://foo.exmaple上要訪問 http://bar.other上的資源。則
//request headers上添加
Origin: http://foo.example
//response headers返回
Access-Control-Allow-Origin: *
由於在 http://foo.example 上要訪問 http://bar.other,所以http://bar.other必須要告訴其...,能不能訪問我。*號表示該資源可以被任意外域訪問。
如果返回
Access-Control-Allow-Origin: http://foo.example
表示,http://bar.other的資源只能被http://foo.example訪問,其他網站不能訪問我。
2、非“簡單請求”(觸發CORS預檢請求)
滿足下列條件之一:
第一條: http請求方式爲下列:PUT | DELETE | CONNECT | OPTIONS | TRACE | PATCH
第二條: Content-Type 的值不屬於下列之一: application/x-www-form-urlencoded | multipart/form-data | text/plain
第三條: 在請求中,發送自定義的頭部(如X-Modified)
如果在 http://foo.exmaple 上要訪問 http://bar.other/resources/po... 上的資源。且 request headers 中 Content-Type爲application/xml,請求method爲post。
那麼此請求是個“非簡單請求”。首先瀏覽器會自動發送帶有options選項的預檢請求,然後發送實際請求
//預檢請求request headers
OPTIONS /resources/post-here/ HTTP/1.1(自動,不需要設置)
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type
//預檢請求返回response headers
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Access-Control-Allow-Origin 表明服務器允許任何其他服務器訪問自己。
Access-Control-Allow-Methods 表明服務器允許客戶端使用 POST, GET, OPTIONS 方法發起請求。
Access-Control-Allow-Headers 表明服務器允許請求中攜帶字段 X-PINGOTHER, Content-Type。
Access-Control-Max-Age 表明在86400內,不會再發送預檢請求。
然後瀏覽器接着發送實際請求
POST /resources/post-here/ HTTP/1.1
Content-Type: application/xml; charset=UTF-8
實際請求返回
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
3、附帶身份憑證的請求
一般而言,對於跨域 XMLHttpRequest 或 Fetch 請求,瀏覽器不會發送身份憑證信息。如果要發送憑證信息,需要設置 XMLHttpRequest 的某個特殊標誌位。
var xhr = new XMLHttpRequest();
var url = 'http://xxxxxxxxx';
xhr.open('GET', url, true);
xhr.withCredentials = true; // 設置發送人請求時 攜帶 cookie憑證
xhr.send();
請求發送
GET /resources/access-control-with-credentials/ HTTP/1.1
Cookie: pageAccess=2
請求返回
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.example //在攜帶憑證的請求中,返回不得設置爲*, 必須設置爲具體域名
Access-Control-Allow-Credentials: true //必須攜帶這個,否則響應內容不會返回給請求的發起者
4、CORS幾個response header解釋
4.1 Access-Control-Allow-Origin
它的值只有兩種,要麼*, 好麼具體的域名 <origin>
4.2 Access-Control-Expose-Headers
在跨域訪問時,XMLHttpRequest對象的getResponseHeader()方法只能拿到一些最基本的響應頭:
Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma
無法訪問其他的響應頭(甚至在控制檯network中看不到),如 set-cookie等,如果要訪問其他頭,則需要服務器設置,將能返回的響應頭放入白名單
Access-Control-Expose-Headers: set-cookie
這樣瀏覽器就能夠通過getResponseHeader訪問set-cookie響應頭了
4.3 Access-Control-Max-Age
指定了預檢請求的結果能夠被緩存多久,在此時間內,不會再發起預檢請求。
Access-Control-Max-Age: <seconds>
4.4 Access-Control-Allow-Credentials
指定了當瀏覽器的credentials設置爲true時,是否允許瀏覽器讀取response的內容。