溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

帶你了解HTTP黑科技

發(fā)布時(shí)間:2021-11-05 14:33:51 來源:億速云 閱讀:151 作者:iii 欄目:web開發(fā)

這篇文章主要講解了“帶你了解HTTP黑科技”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“帶你了解HTTP黑科技”吧!

HTTP 內(nèi)容協(xié)商

什么是內(nèi)容協(xié)商

在 HTTP 中,內(nèi)容協(xié)商是一種用于在同一 URL 上提供資源的不同表示形式的機(jī)制。內(nèi)容協(xié)商機(jī)制是指客戶端和服務(wù)器端就響應(yīng)的資源內(nèi)容進(jìn)行交涉,然后提供給客戶端最為適合的資源。內(nèi)容協(xié)商會(huì)以響應(yīng)資源的語言、字符集、編碼方式等作為判斷的標(biāo)準(zhǔn)。

帶你了解HTTP黑科技

內(nèi)容協(xié)商的種類

內(nèi)容協(xié)商主要有以下3種類型:

  •  服務(wù)器驅(qū)動(dòng)協(xié)商(Server-driven Negotiation)

這種協(xié)商方式是由服務(wù)器端進(jìn)行內(nèi)容協(xié)商。服務(wù)器端會(huì)根據(jù)請(qǐng)求首部字段進(jìn)行自動(dòng)處理

  •  客戶端驅(qū)動(dòng)協(xié)商(Agent-driven Negotiation)

這種協(xié)商方式是由客戶端來進(jìn)行內(nèi)容協(xié)商。

  •  透明協(xié)商(Transparent Negotiation)

是服務(wù)器驅(qū)動(dòng)和客戶端驅(qū)動(dòng)的結(jié)合體,是由服務(wù)器端和客戶端各自進(jìn)行內(nèi)容協(xié)商的一種方法。

內(nèi)容協(xié)商的分類有很多種,主要的幾種類型是 Accept、Accept-Charset、Accept-Encoding、Accept-Language、Content-Language。

一般來說,客戶端用 Accept 頭告訴服務(wù)器希望接收什么樣的數(shù)據(jù),而服務(wù)器用 Content 頭告訴客戶端實(shí)際發(fā)送了什么樣的數(shù)據(jù)。

為什么需要內(nèi)容協(xié)商

我們?yōu)槭裁葱枰獌?nèi)容協(xié)商呢?在回答這個(gè)問題前我們先來看一下 TCP 和 HTTP 的不同。

在 TCP / IP 協(xié)議棧里,傳輸數(shù)據(jù)基本上都是 header+body 的格式。但 TCP、UDP 因?yàn)槭莻鬏攲拥膮f(xié)議,它們不會(huì)關(guān)心 body 數(shù)據(jù)是什么,只要把數(shù)據(jù)發(fā)送到對(duì)方就算是完成了任務(wù)。

而 HTTP 協(xié)議則不同,它是應(yīng)用層的協(xié)議,數(shù)據(jù)到達(dá)之后需要告訴應(yīng)用程序這是什么數(shù)據(jù)。當(dāng)然不告訴應(yīng)用這是哪種類型的數(shù)據(jù),應(yīng)用也可以通過不斷嘗試來判斷,但這種方式無疑十分低效,而且有很大幾率會(huì)檢查不出來文件類型。

所以鑒于此,瀏覽器和服務(wù)器需要就數(shù)據(jù)的傳輸達(dá)成一致,瀏覽器需要告訴服務(wù)器自己希望能夠接收什么樣的數(shù)據(jù),需要什么樣的壓縮格式,什么語言,哪種字符集等;而服務(wù)器需要告訴客戶端自己能夠提供的服務(wù)是什么。

所以我們就引出了內(nèi)容協(xié)商的幾種概念,下面依次來進(jìn)行探討

內(nèi)容協(xié)商標(biāo)頭

Accept

接受請(qǐng)求 HTTP 標(biāo)頭會(huì)通告客戶端自己能夠接受的 MIME 類型

那么什么是 MIME 類型呢?在回答這個(gè)問題前你應(yīng)該先了解一下什么是 MIME

MIME: MIME (Multipurpose Internet Mail Extensions) 是描述消息內(nèi)容類型的因特網(wǎng)標(biāo)準(zhǔn)。MIME 消息能包含文本、圖像、音頻、視頻以及其他應(yīng)用程序?qū)S玫臄?shù)據(jù)。

也就是說,MIME 類型其實(shí)就是一系列消息內(nèi)容類型的集合。那么 MIME 類型都有哪些呢?

文本文件: text/html、text/plain、text/css、application/xhtml+xml、application/xml

圖片文件: image/jpeg、image/gif、image/png

視頻文件: video/mpeg、video/quicktime

應(yīng)用程序二進(jìn)制文件: application/octet-stream、application/zip

比如,如果瀏覽器不支持 PNG 圖片的顯示,那 Accept 就不指定image/png,而指定可處理的 image/gif 和 image/jpeg 等圖片類型。

一般 MIME 類型也會(huì)和 q 這個(gè)屬性一起使用,q 是什么?q 表示的是權(quán)重,來看一個(gè)例子

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

這是什么意思呢?若想要給顯示的媒體類型增加優(yōu)先級(jí),則使用 q= 來額外表示權(quán)重值,沒有顯示權(quán)重的時(shí)候默認(rèn)值是1.0 ,我給你列個(gè)表格你就明白了

qMIME
1.0text/html
1.0application/xhtml+xml
0.9application/xml
0.8/

也就是說,這是一個(gè)放置順序,權(quán)重高的在前,低的在后,application/xml;q=0.9 是不可分割的整體。

Accept-Charset

Accept-charset 屬性規(guī)定服務(wù)器處理表單數(shù)據(jù)所接受的字符編碼;Accept-charset 屬性允許你指定一系列字符集,服務(wù)器必須支持這些字符集,從而得以正確解釋表單中的數(shù)據(jù)。

Accept-Charset 沒有對(duì)應(yīng)的標(biāo)頭,服務(wù)器會(huì)把這個(gè)值放在 Content-Type中用 charset=xxx來表示,

例如,瀏覽器請(qǐng)求 GBK 或 UTF-8 的字符集,然后服務(wù)器返回的是 UTF-8 編碼,就是下面這樣

Accept-Charset: gbk, utf-8  Content-Type: text/html; charset=utf-8

Accept-Language

首部字段 Accept-Language 用來告知服務(wù)器用戶代理能夠處理的自然語言集(指中文或英文等),以及自然語言集的相對(duì)優(yōu)先級(jí)??梢淮沃付ǘ喾N自然語言集。和 Accept 首部字段一樣,按權(quán)重值 q= 來表示相對(duì)優(yōu)先級(jí)。

Accept-Language: en-US,en;q=0.5

Accept-Encoding

表示 HTTP 標(biāo)頭會(huì)標(biāo)明客戶端希望服務(wù)端返回的內(nèi)容編碼,這通常是一種壓縮算法。Accept-Encoding 也是屬于內(nèi)容協(xié)商 的一部分,使用并通過客戶端選擇 Content-Encoding 內(nèi)容進(jìn)行返回。

即使客戶端和服務(wù)器都能夠支持相同的壓縮算法,服務(wù)器也可能選擇不壓縮并返回,這種情況可能是由于這兩種情況造成的:

  •  要發(fā)送的數(shù)據(jù)已經(jīng)被壓縮了一次,第二次壓縮并不會(huì)導(dǎo)致發(fā)送的數(shù)據(jù)更小

  •  服務(wù)器過載,無法承受壓縮帶來的性能開銷,通常,如果服務(wù)器使用 CPU 超過 80% ,Microsoft 則建議不要使用壓縮

下面是 Accept-Encoding 的使用方式

Accept-Encoding: gzip  Accept-Encoding: compress  Accept-Encoding: deflate  Accept-Encoding: br  Accept-Encoding: identity  Accept-Encoding: *  Accept-Encoding: deflate, gzip;q=1.0, *;q=0.5

上面的幾種表述方式就已經(jīng)把 Accept-Encoding 的屬性列全了

  •  gzip: 由文件壓縮程序 gzip 生成的編碼格式,使用 Lempel-Ziv編碼(LZ77)和32位CRC的壓縮格式,感興趣的同學(xué)可以讀一下 (https://en.wikipedia.org/wiki...)

  •  compress: 使用Lempel-Ziv-Welch(LZW)算法的壓縮格式,有興趣的同學(xué)可以讀 (https://en.wikipedia.org/wiki...)

  •  deflate: 使用 zlib 結(jié)構(gòu)和 deflate 壓縮算法的壓縮格式,參考 (https://en.wikipedia.org/wiki...) 和 (https://en.wikipedia.org/wiki...)

  •  br: 使用 Brotli 算法的壓縮格式,參考 (https://en.wikipedia.org/wiki...)

  •  不執(zhí)行壓縮或不會(huì)變化的默認(rèn)編碼格式

  •  * : 匹配標(biāo)頭中未列出的任何內(nèi)容編碼,如果沒有列出 Accept-Encoding ,這就是默認(rèn)值,并不意味著支

  持任何算法,只是表示沒有偏好

  •  ;q= 采用權(quán)重 q 值來表示相對(duì)優(yōu)先級(jí),這點(diǎn)與首部字段 Accept 相同。

Content-Type

Content-Type 實(shí)體標(biāo)頭用于指示資源的 MIME 類型。作為響應(yīng),Content-Type 標(biāo)頭告訴客戶端返回的內(nèi)容的內(nèi)容類型實(shí)際上是什么。Content-type 有兩種值 : MIME 類型和字符集編碼,例如

Content-Type: text/html; charset=UTF-8

  在某些情況下,瀏覽器將執(zhí)行 MIME 嗅探,并且不一定遵循此標(biāo)頭的值;為防止此行為,可以將標(biāo)頭 X-Content-Type-Options 設(shè)置為 nosniff。

Content-Encoding

Content-Encoding 實(shí)體標(biāo)頭用于壓縮媒體類型,它讓客戶端知道如何進(jìn)行解碼操作,從而使客戶端獲得 Content-Type 標(biāo)頭引用的 MIME 類型。表示如下

Content-Encoding: gzip  Content-Encoding: compress  Content-Encoding: deflate  Content-Encoding: identity  Content-Encoding: br  Content-Encoding: gzip, identity  Content-Encoding: deflate, gzip

Content-Language

Content-Language 實(shí)體標(biāo)頭用于描述面向受眾的語言,以便使用戶根據(jù)用戶自己的首選語言進(jìn)行區(qū)分。例如

Content-Language: de-DE  Content-Language: en-US  Content-Language: de-DE, en-CA

下面根據(jù)內(nèi)容協(xié)商對(duì)應(yīng)的請(qǐng)求/響應(yīng)標(biāo)頭,我列了一張圖供你參考,注意其中 Accept-Charset 沒有對(duì)應(yīng)的 Content-Charset ,而是通過 Content-Type 來表示。

帶你了解HTTP黑科技

HTTP 認(rèn)證

HTTP 提供了用于訪問控制和身份認(rèn)證的功能,下面就對(duì) HTTP 的權(quán)限和認(rèn)證功能進(jìn)行介紹

通用 HTTP 認(rèn)證框架

RFC 7235 定義了 HTTP 身份認(rèn)證框架,服務(wù)器可以根據(jù)其文檔的定義來檢查客戶端請(qǐng)求??蛻舳艘部梢愿鶕?jù)其文檔定義來提供身份驗(yàn)證信息。

請(qǐng)求/響應(yīng)的工作流程如下:服務(wù)器以401(未授權(quán)) 的狀態(tài)響應(yīng)客戶端告訴客戶端服務(wù)器需要認(rèn)證信息,客戶端提供至少一個(gè) www-Authenticate 的響應(yīng)標(biāo)頭進(jìn)行授權(quán)信息的認(rèn)證。想要通過服務(wù)器進(jìn)行身份認(rèn)證的客戶端可以在請(qǐng)求標(biāo)頭字段中添加認(rèn)證標(biāo)頭進(jìn)行身份認(rèn)證,一般的認(rèn)證過程如下

帶你了解HTTP黑科技

首先客戶端發(fā)起一個(gè) HTTP 請(qǐng)求,不帶有任何認(rèn)證標(biāo)頭,服務(wù)器對(duì)此 HTTP 請(qǐng)求作出響應(yīng),發(fā)現(xiàn)此 HTTP 信息未帶有認(rèn)證憑據(jù),服務(wù)器通過 www-Authenticate標(biāo)頭返回 401 告訴客戶端此請(qǐng)求未通過認(rèn)證。然后客戶端進(jìn)行用戶認(rèn)證,認(rèn)證完畢后重新發(fā)起 HTTP 請(qǐng)求,這次 HTTP 請(qǐng)求帶有用戶認(rèn)證憑據(jù)(注意,整個(gè)身份認(rèn)證的過程必須通過 HTTPS 連接保證安全),到達(dá)服務(wù)器后服務(wù)器會(huì)檢查認(rèn)證信息,如果不符合服務(wù)器認(rèn)證信息,會(huì)返回 403 Forbidden 表示用戶認(rèn)證失敗,如果滿足認(rèn)證信息,則返回 200 OK。

我們知道,客戶端和服務(wù)器之間的 HTTP 連接可以被代理緩存重新發(fā)送,所以認(rèn)證信息也適用于代理服務(wù)器。

代理認(rèn)證

由于資源認(rèn)證和代理認(rèn)證可以共存,因此需要不同的頭和狀態(tài)碼,在代理的情況下,會(huì)返回狀態(tài)碼 407(需要代理認(rèn)證), Proxy-Authenticate 響應(yīng)頭包含至少一個(gè)適用于代理的情況,Proxy-Authorization請(qǐng)求頭用于將證書提供給代理服務(wù)器。下面分別來認(rèn)識(shí)一下這兩個(gè)標(biāo)頭

Proxy-Authenticate

HTTP Proxy-Authenticate 響應(yīng)標(biāo)頭定義了身份驗(yàn)證方法,應(yīng)使用該身份驗(yàn)證方法來訪問代理服務(wù)器后面的資源。它將請(qǐng)求認(rèn)證到代理服務(wù)器,從而允許它進(jìn)一步發(fā)送請(qǐng)求。例如

Proxy-Authenticate: Basic  Proxy-Authenticate: Basic realm="Access to the internal site"

Proxy-Authorization

這個(gè) HTTP 請(qǐng)求標(biāo)頭和上面的 Proxy-Authenticate 拼接很相似,但是概念不同,這個(gè)標(biāo)頭用于向代理服務(wù)器提供憑據(jù),例如

Proxy-Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l

下面是代理服務(wù)器的請(qǐng)求/響應(yīng)認(rèn)證過程

帶你了解HTTP黑科技

這個(gè)過程和通用的過程類似,我們就不再詳細(xì)展開描述了。

禁止訪問

如果代理服務(wù)器收到的有效憑據(jù)不足以獲取對(duì)給定資源的訪問權(quán)限,則服務(wù)器應(yīng)使用403 Forbidden狀態(tài)代碼進(jìn)行響應(yīng)。與 401 Unauthorized 和 407 Proxy Authorization Required 不同,該用戶無法進(jìn)行身份驗(yàn)證。

WWW-Authenticate 和 Proxy-Authenticate 頭

WWW-Authenticate 和 Proxy-Authenticate 響應(yīng)頭定義了獲得對(duì)資源訪問權(quán)限的身份驗(yàn)證方法。他們需要指定使用哪種身份驗(yàn)證方案,以便希望授權(quán)的客戶端知道如何提供憑據(jù)。它們的一般表示形式如下

WWW-Authenticate: <type> realm=<realm>  Proxy-Authenticate: <type> realm=<realm>

我想你從上面看到這里一定會(huì)好奇 <type> 和 realm是什么東西,現(xiàn)在就來解釋下。

  •  <type> 是認(rèn)證協(xié)議,Basic 是下面協(xié)議中最普遍使用的

RFC 7617 中定義了Basic HTT P身份驗(yàn)證方案,該方案將憑據(jù)作為用戶ID /密碼對(duì)傳輸,并使用 base64 進(jìn)行編碼。(感興趣的同學(xué)可以看看 https://tools.ietf.org/html/r...

其他的認(rèn)證協(xié)議主要有

認(rèn)證協(xié)議參考來源
Basic查閱 RFC 7617,base64編碼的憑據(jù)
Bearer查閱 RFC 6750,承載令牌來訪問受 OAuth 2.0保護(hù)的資源
Digest查閱 RFC 7616,F(xiàn)irefox僅支持md5哈希,請(qǐng)參見錯(cuò)誤bug 472823以獲得SHA加密支持
HOBA查閱 RFC 7486
Mutual查閱 RFC 8120
AWS4-HMAC-SHA256查閱 AWS docs

AWS4-HMAC-SHA256 查閱 AWS docs

  •  realm 用于描述保護(hù)區(qū)或指示保護(hù)范圍,這可能是諸如 Access to the staging site(訪問登陸站點(diǎn)) 或者類似的,這樣用戶就可以知道他們要訪問哪個(gè)區(qū)域。

Authorization 和 Proxy-Authorization 標(biāo)頭

Authorization 和 Proxy-Authorization 請(qǐng)求標(biāo)頭包含用于通過代理服務(wù)器對(duì)用戶代理進(jìn)行身份驗(yàn)證的憑據(jù)。在此,再次需要類型,其后是憑據(jù),取決于使用哪種身份驗(yàn)證方案,可以對(duì)憑據(jù)進(jìn)行編碼或加密。一般表示如下

Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l  Proxy-Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l

HTTP 緩存

通過把請(qǐng)求/響應(yīng)緩存起來有助于提升系統(tǒng)的性能,Web 緩存減少了延遲和網(wǎng)絡(luò)傳輸量,因此減少資源獲取鎖需要的時(shí)間。由于鏈路漫長(zhǎng),網(wǎng)絡(luò)時(shí)延不可控,瀏覽器使用 HTTP 獲取資源的成本較高。所以,非常有必要把數(shù)據(jù)緩存起來,下次再請(qǐng)求的時(shí)候盡可能地復(fù)用。當(dāng) Web 緩存在其存儲(chǔ)中具有請(qǐng)求的資源時(shí),它將攔截該請(qǐng)求并直接返回資源,而不是到達(dá)源服務(wù)器重新下載并獲取。這樣做可以實(shí)現(xiàn)兩個(gè)小目標(biāo)

  •  減輕服務(wù)器負(fù)載

  •  提升系統(tǒng)性能

下面我們就一起來探討一下 HTTP 緩存都有哪些

不同類型的緩存

HTTP 緩存有幾種不同的類型,這些可以分為兩個(gè)主要類別:私有緩存 和 共享緩存。

  •  共享緩存:共享緩存是一種緩存,它可以存儲(chǔ)多個(gè)用戶重復(fù)使用的請(qǐng)求/響應(yīng)。

  •  私有緩存:私有緩存也稱為專用緩存,它只適用于單個(gè)用戶。

  •  不緩存過期資源:所有的請(qǐng)求都會(huì)直接到達(dá)服務(wù)器,由服務(wù)器來下載資源并返回。

我們主要探討瀏覽器緩存和代理緩存,但真實(shí)情況不只有這兩種緩存,還有網(wǎng)關(guān)緩存,CDN,反向代理緩存和負(fù)載平衡器,把它們部署在 Web 服務(wù)器上,可以提高網(wǎng)站和 Web 應(yīng)用程序的可靠性,性能和可伸縮性。

不緩存過期資源

不緩存過期資源即瀏覽器和代理不會(huì)緩存過期資源,客戶端發(fā)起的請(qǐng)求會(huì)直接到達(dá)服務(wù)器,可以使用 no-cache 標(biāo)頭代表不緩存過期資源。

帶你了解HTTP黑科技

no-cache 屬于 Cache-Control 通用標(biāo)頭,其一般的表示方法如下

Cache-Control: no-cache

也可以使用 max-age = 0 來實(shí)現(xiàn)不緩存的效果。

Cache-Control: max-age=0

私有緩存

私有緩存只用來緩存單個(gè)用戶,你可能在瀏覽器設(shè)置中看到了 緩存,瀏覽器緩存包含服務(wù)器通過 HTTP 下載下來的所有文檔。這個(gè)高速緩存用于使訪問的文檔可以進(jìn)行前進(jìn)/后退,保存操作而無需重新發(fā)送請(qǐng)求到源服務(wù)器。

帶你了解HTTP黑科技

可以使用 private 來實(shí)現(xiàn)私有緩存,這與 public 的用法相反,緩存服務(wù)器只對(duì)特定的客戶端進(jìn)行緩存,其他客戶端發(fā)送過來的請(qǐng)求,緩存服務(wù)器則不會(huì)返回緩存。它的一般表示方法如下

Cache-Control: private

共享緩存

共享緩存是一種用于存儲(chǔ)要由多個(gè)用戶重用的響應(yīng)緩存。共享緩存一般使用 public 來表示,public 屬性只出現(xiàn)在客戶端響應(yīng)中,表示響應(yīng)可以被任何緩存所緩存。一般表示方法如下

Cache-Control: public

帶你了解HTTP黑科技

緩存控制

HTTP/1.1 中的 Cache-Control 常規(guī)標(biāo)頭字段用于執(zhí)行緩存控制,使用此標(biāo)頭可通過其提供的各種指令來定義緩存策略。下面我們依次介紹一下這些屬性

不緩存

no-store 才是真正意義上的不緩存,每次服務(wù)器接受到客戶端的請(qǐng)求后,都會(huì)返回最新的資源給客戶端。

Cache-Control: no-store

緩存但需要驗(yàn)證

同上面的 不緩存過期資源

私有和共享緩存

同上

緩存過期

緩存中一個(gè)很重要的指令就是max-age,這是資源被視為新鮮的最長(zhǎng)時(shí)間 ,與 Expires 相反,此指令是相對(duì)于請(qǐng)求時(shí)間的。對(duì)于應(yīng)用程序中不會(huì)更改的文件,通常可以添加主動(dòng)緩存。下面是 mag-age 的表示

Cache-Control: max-age=31536000

緩存驗(yàn)證

must-revalidate 表示緩存必須在使用之前驗(yàn)證過時(shí)資源的狀態(tài),并且不應(yīng)使用過期的資源。

Cache-Control: must-revalidate

下面是一個(gè)緩存驗(yàn)證圖

帶你了解HTTP黑科技

什么是新鮮的數(shù)據(jù)

一旦資源存儲(chǔ)在緩存中,理論上就可以永遠(yuǎn)被緩存使用。但是不管是瀏覽器緩存還是代理緩存,其存儲(chǔ)空間是有限的,所以緩存會(huì)定期進(jìn)行清除,這個(gè)過程叫做 緩存回收(cache eviction) (自譯)。另一方面,服務(wù)器上的緩存也會(huì)定期進(jìn)行更新,HTTP 作為應(yīng)用層的協(xié)議,它是一種客戶-服務(wù)器模式,HTTP 是無狀態(tài)的協(xié)議,因此當(dāng)資源發(fā)生更改時(shí),服務(wù)器無法通知緩存和客戶端。因此服務(wù)器必須通過某種方式告知客戶端緩存已經(jīng)被更新。服務(wù)器會(huì)提供過期時(shí)間這個(gè)概念,告知客戶端在此到期時(shí)間之前,資源是新鮮的,也就是未更改過的。在此到期時(shí)間的范圍之外,資源已過時(shí)。過期算法(Eviction algorithms) 通常會(huì)將新資源優(yōu)先于陳舊資源使用。

這里需要注意一下,過期的資源并不會(huì)被回收或忽略,當(dāng)高速緩存接收到過期資源時(shí),它會(huì)使用 If-None-Match 轉(zhuǎn)發(fā)此請(qǐng)求,以檢查它是否仍然有效。如果有效,服務(wù)器會(huì)返回 304 Not Modified響應(yīng)頭并且沒有任何響應(yīng)體,從而節(jié)省了一些帶寬。

下面是使用共享緩存代理的過程

帶你了解HTTP黑科技

這個(gè)圖應(yīng)該比較好理解,只說一下 Age 的作用,Age 是 HTTP 響應(yīng)標(biāo)頭告訴客戶端源服務(wù)器在多久之前創(chuàng)建了響應(yīng),它的單位為秒,Age 標(biāo)頭通常接近于0,如果是0則可能是從源服務(wù)器獲取的,如果不是表示可能是由代理服務(wù)器創(chuàng)建,那么 Age 的值表示的是緩存后的響應(yīng)再次發(fā)起認(rèn)證到認(rèn)證完成的時(shí)間值。

緩存的有效性是由多個(gè)標(biāo)頭來共同決定的,而并非某一個(gè)標(biāo)頭來決定。如果指定了 Cache-control:max-age=N ,那么緩存會(huì)保存 N 秒。如果這個(gè)通用標(biāo)頭不存在的話,則會(huì)檢查是否存在 Expires 標(biāo)頭。如果 Exprires 標(biāo)頭存在,那么它的值減去 Date 標(biāo)頭的值就可以確定其有效性。最后,如果max-age 和 expires 都不存在,就去尋找 Last-Modified 標(biāo)頭,如果存在此標(biāo)頭,則高速緩存的有效性等于 Date 標(biāo)頭的值減去 Last-modified 標(biāo)頭的值除以10。

緩存驗(yàn)證

當(dāng)?shù)竭_(dá)緩存資源的有效期時(shí),將對(duì)其進(jìn)行驗(yàn)證或再次獲取。僅當(dāng)服務(wù)器提供了強(qiáng)驗(yàn)證器或弱驗(yàn)證器時(shí),才可以進(jìn)行驗(yàn)證。

當(dāng)用戶按下重新加載按鈕時(shí),將觸發(fā)重新驗(yàn)證。如果緩存的響應(yīng)包含 Cache-control:must-revalidate標(biāo)頭,則在正常瀏覽下也會(huì)觸發(fā)該事件。另一個(gè)因素是 高級(jí) -> 緩存首選項(xiàng) 面板中的緩存驗(yàn)證首選項(xiàng)。有一個(gè)選項(xiàng)可在每次加載文檔時(shí)強(qiáng)制進(jìn)行驗(yàn)證。

Etag

我們上面提到了強(qiáng)驗(yàn)證器和弱驗(yàn)證器,實(shí)現(xiàn)驗(yàn)證器功能的標(biāo)頭正式 Etag 的作用,這意味著 HTTP 用戶代理(例如瀏覽器)不知道該字符串表示什么,并且無法預(yù)測(cè)其值。如果 Etag 標(biāo)頭是資源響應(yīng)的一部分,則客戶端可以在未來請(qǐng)求的標(biāo)頭中發(fā)出 If-None-Match,以驗(yàn)證緩存的資源。

Last-Modified 響應(yīng)標(biāo)頭可以用作弱驗(yàn)證器,因?yàn)樗挥?秒可以分辨的時(shí)間。如果響應(yīng)中存在 Last-Modified 標(biāo)頭,則客戶端可以發(fā)出 If-Modified-Since 請(qǐng)求標(biāo)頭來驗(yàn)證緩存資源。(關(guān)于 Etag 更多我們會(huì)在條件請(qǐng)求介紹)

避免碰撞

通過使用 Etag 和 If-Match 標(biāo)頭,你可以檢測(cè)避免碰撞。

例如,在編輯 MDN 時(shí),將對(duì)當(dāng)前 Wiki 內(nèi)容進(jìn)行哈希處理并將其放入響應(yīng)中的 Etag 中

Etag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

當(dāng)將更改保存到 Wiki 頁面(發(fā)布數(shù)據(jù))時(shí),POST 請(qǐng)求將包含 If-Match 標(biāo)頭,其中包含 Etag 值以檢查有效性。

If-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

如果哈希值不匹配,則表示文檔已在中間進(jìn)行了編輯,并返回 412 Precondition Failed 錯(cuò)誤。

緩存未占用資源

Etag 標(biāo)頭的另一個(gè)典型用法是緩存未更改的資源,如果用戶再次訪問給定的 URL(已設(shè)置Etag),并且該 URL過時(shí),則客戶端將在 If-None-Match 標(biāo)頭字段中發(fā)送其 Etag 的值

If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"

服務(wù)器將客戶端的 Etag(通過 If-None-Match 發(fā)送)與 Etag 進(jìn)行比較,以獲取其當(dāng)前資源版本,如果兩個(gè)值都匹配(即資源未更改),則服務(wù)器會(huì)發(fā)回 304 Not Modified 狀態(tài),沒有主體,它告訴客戶端響應(yīng)的緩存仍然可以使用。

HTTP CROS 跨域

CROS 的全稱是 Cross-Origin Resource Sharing(CROS),中文譯為 跨域資源共享,它是一種機(jī)制。是一種什么機(jī)制呢?它是一種讓運(yùn)行在一個(gè)域(origin)上的 Web 應(yīng)用被準(zhǔn)許訪問來自不同源服務(wù)器上指定資源的機(jī)制。在搞懂這個(gè)機(jī)制前,你需要線了解什么是 域(origin)

Origin

Web 概念中域(Origin) 的內(nèi)容由scheme(protocol) - 協(xié)議,host(domain) - 主機(jī)和用于訪問它的 URL port - 端口定義。僅僅當(dāng) scheme 、host、port 都匹配時(shí),兩個(gè)對(duì)象才有相同的來源。這種協(xié)議相同,域名相同,端口相同的安全策略也被稱為 同源策略(Same Origin Policy)。某些操作僅限于具有相同來源的內(nèi)容,可以使用 CORS 取消此限制。

跨域的特點(diǎn)

  •  下面是跨域問題的例子,看看你是否清楚什么是跨域了 

(1) http://example.com/app1/index.html  (2) http://example.com/app2/index.html

上面這兩個(gè) URL 是否具有跨域問題呢?

上面兩個(gè) URL 是不具有跨域問題的,因?yàn)檫@兩個(gè) URL 具有相同的協(xié)議(scheme)和主機(jī)(host)

  •  那么下面這兩個(gè)是否具有跨域問題呢? 

http://Example.com:80  http://example.com

這兩個(gè) URL 也不具有跨域問題,為什么不具有,端口不一樣啊。其實(shí)它們兩個(gè)端口是一樣的。

或許你會(huì)認(rèn)為這兩個(gè) URL 是不一樣的,放心,關(guān)于一樣不一樣的論據(jù)我給你拋出來了

協(xié)議和域名部分是不區(qū)分大小寫的,但是路徑部分則根據(jù)服務(wù)器平臺(tái)而定。Windows 和 Mac OS X 系統(tǒng)是不區(qū)分大小寫的,而采用UNIX和Linux系的服務(wù)器系統(tǒng)是區(qū)分大小寫的,

也就是說上面的 Example.com 和 example.com 其實(shí)是一個(gè)網(wǎng)址,并且由于兩個(gè)地址具有相同的 scheme 和 host ,默認(rèn)情況下服務(wù)器通過端口80傳遞 HTTP 內(nèi)容,所以上面這兩個(gè)地址也是相同的。

  •     下面這兩個(gè) URL 地址是否具有跨域問題? 

http://example.com/app1  https://example.com/app2

這兩個(gè) URL 的 scheme 不同,所以這兩個(gè) URL 具有跨域問題

  •  再看下面這三個(gè) URL 是否具有跨域問題 

http://example.com  http://www.example.com  http://myapp.example.com

這三個(gè) URL 也是具有跨域問題的,因?yàn)樗鼈冸`屬于不通服務(wù)器的主機(jī) host。

  •  下面這兩個(gè) URL 是否具有跨域問題 

http://example.com  http://example.com:8080

這兩個(gè) URL 也是具有跨域問題,因?yàn)檫@兩個(gè) URL 的默認(rèn)端口不一樣。

同源策略

處于安全的因素,瀏覽器限制了從腳本發(fā)起跨域的 HTTP 請(qǐng)求。 XMLHttpRequest 和其他 Fetch 接口 會(huì)遵循 同源策略(same-origin policy)。也就是說使用這些 API 的應(yīng)用程序想要請(qǐng)求相同的資源,那么他們應(yīng)該具有相同的來源,除非來自其他來源的響應(yīng)包括正確的 CORS 標(biāo)頭也可以。

同源策略是一種很重要的安全策略,它限制了從一個(gè)來源加載的文檔或腳本如何與另一個(gè)來源的資源進(jìn)行交互。 它有助于隔離潛在的惡意文檔,減少可能的攻擊媒介。

我們上面提到,如果兩個(gè) URL 具有相同的協(xié)議、主機(jī)和端口號(hào)(如果指定)的話,那么兩個(gè) URL 具有相同的來源。下面有一些實(shí)例,你判斷一下是不是具有相同的來源

目標(biāo)來源 http://store.company.com/dir/page.html

URLOutcomeReason
http://store.company.com/dir2...相同來源只有path不同
http://store.company.com/dir/...相同來源只有path不同
https://store.company.com/pag...不同來源協(xié)議不通
http://store.company.com:81/dir/page.html不同來源默認(rèn)端口不同
http://news.company.com/dir/p...不同來源主機(jī)不同

現(xiàn)在我?guī)阏J(rèn)識(shí)了兩遍不同的源,現(xiàn)在你應(yīng)該知道如何區(qū)分兩個(gè) URL 是否屬于同一來源了吧!

好,你現(xiàn)在知道了什么是跨域問題,現(xiàn)在我要問你,哪些請(qǐng)求會(huì)產(chǎn)生跨域請(qǐng)求呢?這是我們下面要討論的問題

跨域請(qǐng)求

跨域請(qǐng)求可能會(huì)從下面這幾種請(qǐng)求中發(fā)出:

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  調(diào)用 XMLHttpRequest 或者 Fetch api。

XMLHttpRequest 是什么?(我是后端程序員,前端不太懂,簡(jiǎn)單解釋下,如果解釋的不好,還請(qǐng)前端大佬們不要胖揍我)

所有的現(xiàn)代瀏覽器都有一個(gè)內(nèi)置的 XMLHttpReqeust 對(duì)象,這個(gè)對(duì)象可以用于從服務(wù)器請(qǐng)求數(shù)據(jù)。

XMLHttpReqeust 對(duì)于開發(fā)人員來說很重要,XMLHttpReqeust 對(duì)象可以用來做下面這些事情

  •  更新網(wǎng)頁無需重新刷新頁面

  •  頁面加載后從服務(wù)器請(qǐng)求數(shù)據(jù)

  •  頁面加載后從服務(wù)端獲取數(shù)據(jù)

  •  在后臺(tái)將數(shù)據(jù)發(fā)送到服務(wù)器

使用 XMLHttpRequest(XHR) 對(duì)象與服務(wù)器進(jìn)行交互,你可以從 URL 檢索數(shù)據(jù)從而不必刷新整個(gè)頁面,這使網(wǎng)頁可以更新頁面的一部分,而不會(huì)中斷用戶的操作。XMLHttpRequest 在 AJAX 異步編程中使用很廣泛。

再來說一下 Fetch API 是什么,F(xiàn)etch 提供了請(qǐng)求和響應(yīng)對(duì)象(以及其他網(wǎng)絡(luò)請(qǐng)求)的通用定義。它還提供了相關(guān)概念的定義,例如 CORS 和 HTTP Origin 頭語義,并在其他地方取代了它們各自的定義。

  1. 鴻蒙官方戰(zhàn)略合作共建——HarmonyOS技術(shù)社區(qū)

  2.  Web 字體(用于 CSS 中@ font-face中的跨域字體使用),以便服務(wù)器可以部署 TrueType 字體,這些字體只能由允許跨站點(diǎn)加載和使用的網(wǎng)站使用。

  3.  WebGL 紋理

  4.  使用 drawImage() 繪制到畫布上的圖像/視頻幀

  5.  圖片的 CSS 形狀

跨域功能概述

跨域資源共享標(biāo)準(zhǔn)通過添加新的 HTTP 標(biāo)頭來工作,這些標(biāo)頭允許服務(wù)器描述允許哪些來源從 Web 瀏覽器讀取信息。另外,對(duì)于可能導(dǎo)致服務(wù)器數(shù)據(jù)產(chǎn)生副作用的 HTTP 請(qǐng)求方法(尤其是 GET 或者具有某些 MIME 類型 POST 方法以外 HTTP 方法),該規(guī)范要求瀏覽器預(yù)檢請(qǐng)求,使用 HTTP OPTIONS 請(qǐng)求方法從服務(wù)器請(qǐng)求受支持的方法,然后在服務(wù)器批準(zhǔn)后發(fā)送實(shí)際請(qǐng)求。服務(wù)器還可以通知客戶端是否應(yīng)與請(qǐng)求一起發(fā)送憑據(jù)(例如 Cookies 和 HTTP 身份驗(yàn)證)。

注意:CORS 故障會(huì)導(dǎo)致錯(cuò)誤,但是出于安全原因,該錯(cuò)誤的詳細(xì)信息不適用于 JavaScript。 所有代碼都知道發(fā)生了錯(cuò)誤。 確定具體出問題的唯一方法是查看瀏覽器的控制臺(tái)以獲取詳細(xì)信息。

訪問控制

下面我會(huì)和大家探討三種方案,這些方案都演示了跨域資源共享的工作方式。所有這些示例都使用XMLHttpRequest,它可以在任何支持的瀏覽器中發(fā)出跨站點(diǎn)請(qǐng)求。

簡(jiǎn)單請(qǐng)求

一些請(qǐng)求不會(huì)觸發(fā) CORS預(yù)檢 (關(guān)于預(yù)檢我們后面再介紹)。簡(jiǎn)單請(qǐng)求是滿足一下所有條件的請(qǐng)求

  •  允許以下的方法:GET、HEAD和 POST

  •  除了由用戶代理自動(dòng)設(shè)置的標(biāo)頭(例如 Connection、User-Agent 或者在 Fetch 規(guī)范中定義為禁止標(biāo)頭名稱的其他標(biāo)頭)外,唯一允許手動(dòng)設(shè)置的標(biāo)頭是那些 Fetch 規(guī)范將其定義為 CORS安全列出的請(qǐng)求標(biāo)頭 ,它們是:

    •   Accept

    •   Accept-Language

    •   Content-Language

    •   Content-Type(下面會(huì)介紹)

    •   DPR

    •   Downlink

    •   Save-Data

    •   Viewport-Width

    •   Width

  •  Content-Type 標(biāo)頭的唯一允許的值是

    •   application/x-www-form-urlencoded

    •   multipart/form-data

    •   text/plain

  •  沒有在請(qǐng)求中使用的任何 XMLHttpRequestUpload 對(duì)象上注冊(cè)事件偵聽器;這些可以使用XMLHttpRequest.upload 屬性進(jìn)行訪問。

  •  請(qǐng)求中未使用 ReadableStream對(duì)象。

例如,假定 web 內(nèi)容 https://foo.example 想要獲取 https://bar.other 域的資源,那么 JavaScript 中的代碼可能會(huì)像下面這樣寫

const xhr = new XMLHttpRequest();    const url = 'https://bar.other/resources/public-data/';       xhr.open('GET', url);    xhr.onreadystatechange = someHandler;    xhr.send();

這使用 CORS 標(biāo)頭來處理特權(quán),從而在客戶端和服務(wù)器之間執(zhí)行某種轉(zhuǎn)換。

帶你了解HTTP黑科技

讓我們看看在這種情況下瀏覽器將發(fā)送到服務(wù)器的內(nèi)容,并讓我們看看服務(wù)器如何響應(yīng):

GET /resources/public-data/ HTTP/1.1  Host: bar.other  User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8  Accept-Language: en-us,en;q=0.5  Accept-Encoding: gzip,deflate  Connection: keep-alive  Origin: https://foo.example

注意請(qǐng)求的標(biāo)頭 Origin ,它表明調(diào)用來自于 https://foo.example。讓我們看看服務(wù)器是如何響應(yīng)的

HTTP/1.1 200 OK  Date: Mon, 01 Dec 2008 00:23:53 GMT  Server: Apache/2  Access-Control-Allow-Origin: *  Keep-Alive: timeout=2, max=100  Connection: Keep-Alive  Transfer-Encoding: chunked  Content-Type: application/xml  [&hellip;XML Data&hellip;]

服務(wù)端發(fā)送 Access-Control-Allow-Origin 作為響應(yīng)。使用 Origin 標(biāo)頭和 Access-Control-Allow-Origin 展示了最簡(jiǎn)單的訪問控制協(xié)議。在這個(gè)事例中,服務(wù)端使用 Access-Control-Allow-Origin 作為響應(yīng),也就說明該資源可以被任何域訪問。

如果位于https://bar.other的資源所有者希望將對(duì)資源的訪問限制為僅來自https://foo.example的請(qǐng)求,他們應(yīng)該發(fā)送如下響應(yīng)

Access-Control-Allow-Origin: https://foo.example

現(xiàn)在除了 https://foo.example 之外的任何域都無法以跨域方式訪問到 https://bar.other 的資源。

預(yù)檢請(qǐng)求

和上面探討的簡(jiǎn)單請(qǐng)求不同,預(yù)檢請(qǐng)求首先通過 OPTIONS 方法向另一個(gè)域上的資源發(fā)送 HTTP 請(qǐng)求,用來確定實(shí)際請(qǐng)求是否可以安全的發(fā)送??缯军c(diǎn)這樣被預(yù)檢,因?yàn)樗鼈兛赡軙?huì)影響用戶數(shù)據(jù)。

下面是一個(gè)預(yù)檢事例

const xhr = new XMLHttpRequest();  xhr.open('POST', 'https://bar.other/resources/post-here/');  xhr.setRequestHeader('X-PINGOTHER', 'pingpong');  xhr.setRequestHeader('Content-Type', 'application/xml');  xhr.onreadystatechange = handler;  xhr.send('<person><name>Arun</name></person>');

上面的事例創(chuàng)建了一個(gè) XML 請(qǐng)求體用來和 POST 請(qǐng)求一起發(fā)送。此外,設(shè)置了非標(biāo)準(zhǔn)請(qǐng)求頭 X-PINGOTHER ,這個(gè)標(biāo)頭不是 HTTP/1.1 的一部分,但通常對(duì) Web 程序很有用。由于請(qǐng)求的 Content-Type 使用 application/xml,并且設(shè)置了自定義標(biāo)頭,因此該請(qǐng)求被預(yù)檢。如下圖所示

帶你了解HTTP黑科技

如下所述,實(shí)際的 POST 請(qǐng)求不包含 Access-Control-Request- * 標(biāo)頭;只有 OPTIONS 請(qǐng)求才需要它們。

下面我們來看一下完整的客戶端/服務(wù)器交互,首先是預(yù)檢請(qǐng)求/響應(yīng)

OPTIONS /resources/post-here/ HTTP/1.1  Host: bar.other  User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8  Accept-Language: en-us,en;q=0.5  Accept-Encoding: gzip,deflate  Connection: keep-alive  Origin: http://foo.example  Access-Control-Request-Method: POST  Access-Control-Request-Headers: X-PINGOTHER, Content-Type
HTTP/1.1 204 No Content  Date: Mon, 01 Dec 2008 01:15:39 GMT  Server: Apache/2  Access-Control-Allow-Origin: https://foo.example  Access-Control-Allow-Methods: POST, GET, OPTIONS  Access-Control-Allow-Headers: X-PINGOTHER, Content-Type  Access-Control-Max-Age: 86400  Vary: Accept-Encoding, Origin  Keep-Alive: timeout=2, max=100  Connection: Keep-Alive

上面的1 -11 行代表預(yù)檢請(qǐng)求,預(yù)檢請(qǐng)求使用 OPYIIONS 方法,瀏覽器根據(jù)上面的 JavaScript 代碼段所使用的請(qǐng)求參數(shù)確定是否需要發(fā)送此請(qǐng)求,以便服務(wù)器可以響應(yīng)是否可以使用實(shí)際請(qǐng)求參數(shù)發(fā)送請(qǐng)求。OPTIONS 是一種 HTTP / 1.1方法,用于確定來自服務(wù)器的更多信息,并且是一種安全的方法,這意味著它不能用于更改資源。請(qǐng)注意,與 OPTIONS 請(qǐng)求一起,還發(fā)送了另外兩個(gè)請(qǐng)求標(biāo)頭(分別是第9行和第10行)

Access-Control-Request-Method: POST  Access-Control-Request-Headers: X-PINGOTHER, Content-Type

Access-Control-Request-Method 標(biāo)頭作為預(yù)檢請(qǐng)求的一部分通知服務(wù)器,當(dāng)發(fā)送實(shí)際請(qǐng)求時(shí),將使用POST 請(qǐng)求方法發(fā)送該請(qǐng)求。

Access-Control-Request-Headers 標(biāo)頭通知服務(wù)器,當(dāng)發(fā)送請(qǐng)求時(shí),它將與X-PINGOTHER 和 Content-Type 自定義標(biāo)頭一起發(fā)送。服務(wù)器可以確定這種情況下是否接受請(qǐng)求。

下面的 1 - 11行是服務(wù)器發(fā)回的響應(yīng),表示POST 請(qǐng)求和 X-PINGOTHER 是可以接受的,我們著重看一下下面這幾行

Access-Control-Allow-Origin: http://foo.example  Access-Control-Allow-Methods: POST, GET, OPTIONS  Access-Control-Allow-Headers: X-PINGOTHER, Content-Type  Access-Control-Max-Age: 86400

服務(wù)器完成響應(yīng)表明源 http://foo.example 是可以接受的 URL,能夠允許 POST、GET、OPTIONS 進(jìn)行請(qǐng)求,允許自定義標(biāo)頭 X-PINGOTHER, Content-Type。最后,Access-Control-Max-Age 以秒為單位給出一個(gè)值,這個(gè)值表示對(duì)預(yù)檢請(qǐng)求的響應(yīng)可以緩存多長(zhǎng)時(shí)間,在此期間內(nèi)無需發(fā)送其他預(yù)檢請(qǐng)求。

完成預(yù)檢請(qǐng)求后,將發(fā)送實(shí)際請(qǐng)求:

POST /resources/post-here/ HTTP/1.1  Host: bar.other  User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8  Accept-Language: en-us,en;q=0.5  Accept-Encoding: gzip,deflate  Connection: keep-alive  X-PINGOTHER: pingpong  Content-Type: text/xml; charset=UTF-8  Referer: https://foo.example/examples/preflightInvocation.html  Content-Length: 55  Origin: https://foo.example  Pragma: no-cache  Cache-Control: no-cache  <person><name>Arun</name></person>
HTTP/1.1 200 OK  Date: Mon, 01 Dec 2008 01:15:40 GMT  Server: Apache/2  Access-Control-Allow-Origin: https://foo.example  Vary: Accept-Encoding, Origin  Content-Encoding: gzip  Content-Length: 235  Keep-Alive: timeout=2, max=99  Connection: Keep-Alive  Content-Type: text/plain  [Some GZIP'd payload]

正式響應(yīng)中很多標(biāo)頭我們?cè)谥暗奈恼乱呀?jīng)探討過了,本篇不再做詳細(xì)的介紹,讀者可以參考

你還在為 HTTP 的這些概念頭疼嗎? 查閱

帶憑證的請(qǐng)求

XMLHttpRequest 或 Fetch 和 CORS 最有趣的功能就是能夠發(fā)出知道 HTTP Cookie 和 HTTP 身份驗(yàn)證的 憑證 請(qǐng)求。默認(rèn)情況下,在跨站點(diǎn) XMLHttpRequest 或 Fetch 調(diào)用中,瀏覽器將不發(fā)送憑據(jù)。調(diào)用 XMLHttpRequest對(duì)象或 Request 構(gòu)造函數(shù)時(shí)必須設(shè)置一個(gè)特定的標(biāo)志。

在下面這個(gè)例子中,最初從 http://foo.example 加載的內(nèi)容對(duì)設(shè)置了 Cookies 的 http://bar.other 上的資源進(jìn)行了簡(jiǎn)單的 GET 請(qǐng)求, foo.example 上可能的代碼如下

const invocation = new XMLHttpRequest();  const url = 'http://bar.other/resources/credentialed-content/';     function callOtherDomain() {    if (invocation) {      invocation.open('GET', url, true);      invocation.withCredentials = true;      invocation.onreadystatechange = handler;      invocation.send();     }  }

第7行顯示 XMLHttpRequest 上的標(biāo)志,必須設(shè)置該標(biāo)志才能使用 Cookie 進(jìn)行調(diào)用。默認(rèn)情況下,調(diào)用是不在使用 Cookie 的情況下進(jìn)行的。由于這是一個(gè)簡(jiǎn)單的 GET 請(qǐng)求,因此不會(huì)進(jìn)行預(yù)檢,但是瀏覽器將拒絕任何沒有 Access-Control-Allow-Credentials 的響應(yīng):標(biāo)頭為true,指的是響應(yīng)不會(huì)返回 web 頁面的內(nèi)容。

上面的請(qǐng)求用下圖可以表示

帶你了解HTTP黑科技

這是客戶端和服務(wù)器之間的示例交換:

GET /resources/access-control-with-credentials/ HTTP/1.1  Host: bar.other  User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8  Accept-Language: en-us,en;q=0.5  Accept-Encoding: gzip,deflate  Connection: keep-alive  Referer: http://foo.example/examples/credential.html  Origin: http://foo.example  Cookie: pageAccess=2
HTTP/1.1 200 OK  Date: Mon, 01 Dec 2008 01:34:52 GMT  Server: Apache/2  Access-Control-Allow-Origin: https://foo.example  Access-Control-Allow-Credentials: true  Cache-Control: no-cache  Pragma: no-cache  Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT  Vary: Accept-Encoding, Origin  Content-Encoding: gzip  Content-Length: 106  Keep-Alive: timeout=2, max=100  Connection: Keep-Alive  Content-Type: text/plain  [text/plain payload]

上面第10行包含指向http://bar.other 上的內(nèi)容 Cookie,但是如果 bar.other 沒有以 Access-Control-Allow-Credentials:true 響應(yīng)(下面第五行),響應(yīng)將被忽略,并且不能使用網(wǎng)站返回的內(nèi)容。

請(qǐng)求憑證和通配符

當(dāng)回應(yīng)憑證請(qǐng)求時(shí),服務(wù)器必須在 Access-Control-Allow-Credentials 中指定一個(gè)來源,而不能直接寫* 通配符

因?yàn)樯厦媸纠a中的請(qǐng)求標(biāo)頭包含 Cookie 標(biāo)頭,如果 Access-Control-Allow-Credentials 中是指定的通配符 * 的話,請(qǐng)求會(huì)失敗。

注意上面示例中的 Set-Cookie 響應(yīng)標(biāo)頭還設(shè)置了另外一個(gè)值,如果發(fā)生故障,將引發(fā)異常(取決于所使用的API)。

HTTP 響應(yīng)標(biāo)頭

下面會(huì)列出一些服務(wù)器跨域共享規(guī)范定義的 HTTP 標(biāo)頭,上面簡(jiǎn)單概述了一下,現(xiàn)在一起來認(rèn)識(shí)一下,主要會(huì)介紹下面這些

  •  Access-Control-Allow-Origin

  •  Access-Control-Allow-Credentials

  •  Access-Control-Allow-Headers

  •  Access-Control-Allow-Methods

  •  Access-Control-Expose-Headers

  •  Access-Control-Max-Age

  •  Access-Control-Request-Headers

  •  Access-Control-Request-Method

  •  Origin

Access-Control-Allow-Origin

Access-Control-Allow-Origin 是 HTTP 響應(yīng)標(biāo)頭,指示響應(yīng)是否能夠和給定的源共享資源。Access-Control-Allow-Origin 指定單個(gè)資源會(huì)告訴瀏覽器允許指定來源訪問資源。對(duì)于沒有憑據(jù)的請(qǐng)求 *通配符,告訴瀏覽器允許任何源訪問資源。

例如,如果要允許源 https://mozilla.org 的代碼訪問資源,可以使用如下的指定方式

Access-Control-Allow-Origin: https://mozilla.org  Vary: Origin

如果服務(wù)器指定單個(gè)來源而不是*通配符,則服務(wù)器還應(yīng)在 Vary 響應(yīng)標(biāo)頭中包含該來源。

Access-Control-Allow-Credentials

Access-Control-Allow-Credentials 是 HTTP 的響應(yīng)標(biāo)頭,這個(gè)標(biāo)頭告訴瀏覽器,當(dāng)包含憑證請(qǐng)求(Request.credentials)時(shí)是否將響應(yīng)公開給前端 JavaScript 代碼。

這時(shí)候你會(huì)問到 Request.credentials 是什么玩意?不要著急,來給你看一下,首先來看 Request 是什么玩意,

實(shí)際上,Request 是 Fetch API 的一類接口代表著資源請(qǐng)求。一般創(chuàng)建 Request 對(duì)象有兩種方式

  •  使用 Request() 構(gòu)造函數(shù)創(chuàng)建一個(gè) Request 對(duì)象

  •  還可以通過 FetchEvent.request api 操作來創(chuàng)建

再來說下 Request.credentials 是什么意思,Request 接口的憑據(jù)只讀屬性指示在跨域請(qǐng)求的情況下,用戶代理是否應(yīng)從其他域發(fā)送 cookie。(其他 Request 對(duì)象的方法詳見 https://developer.mozilla.org...)

當(dāng)發(fā)送的是憑證模式的請(qǐng)求包含 (Request.credentials)時(shí),如果 Access-Control-Allow-Credentials 值為 true,瀏覽器將僅向前端 JavaScript 代碼公開響應(yīng)。

Access-Control-Allow-Credentials: true

憑證一般包括 cookie、認(rèn)證頭和 TLS 客戶端證書

當(dāng)用作對(duì)預(yù)檢請(qǐng)求響應(yīng)的一部分時(shí),這表明是否可以使用憑據(jù)發(fā)出實(shí)際請(qǐng)求。注意簡(jiǎn)單的 GET 請(qǐng)求不會(huì)進(jìn)行預(yù)檢。

可以參考一個(gè)實(shí)際的例子 https://www.jianshu.com/p/ea4...

Access-Control-Allow-Headers

Access-Control-Allow-Headers 是一個(gè)響應(yīng)標(biāo)頭,這個(gè)標(biāo)頭用來響應(yīng)預(yù)檢請(qǐng)求,它發(fā)出實(shí)際請(qǐng)求時(shí)可以使用哪些HTTP標(biāo)頭。

示例

  •  自定義標(biāo)頭

這是 Access-Control-Allow-Headers 標(biāo)頭的示例。它表明除了像 CROS 安全列出的請(qǐng)求標(biāo)頭外,對(duì)服務(wù)器的 CROS 請(qǐng)求還支持名為 X-Custom-Header 的自定義標(biāo)頭。

Access-Control-Allow-Headers: X-Custom-Header
  •  多個(gè)標(biāo)頭

這個(gè)例子展示了 Access-Control-Allow-Headers 如何使用多個(gè)標(biāo)頭

Access-Control-Allow-Headers: X-Custom-Header, Upgrade-Insecure-Requests
  •  繞過其他限制

盡管始終允許使用 CORS 安全列出的請(qǐng)求標(biāo)頭,并且通常不需要在 Access-Control-Allow-Headers 中列出這些標(biāo)頭,但是無論如何列出它們都將繞開適用的其他限制。

Access-Control-Allow-Headers: Accept

這里你可能會(huì)有疑問,哪些是 CORS 列出的安全標(biāo)頭?(別嫌累,就是這么麻煩)

有下面這些 Accep、Accept-Language、Content-Language、Content-Type ,當(dāng)且僅當(dāng)包含這些標(biāo)頭時(shí),無需在 CORS 上下文中發(fā)送預(yù)檢請(qǐng)求。

Access-Control-Allow-Methods

Access-Control-Allow-Methods 也是響應(yīng)標(biāo)頭,它指定了哪些訪問資源的方法可以使用預(yù)檢請(qǐng)求。例如

Access-Control-Allow-Methods: POST, GET, OPTIONS  Access-Control-Allow-Methods: *

Access-Control-Expose-Headers

Access-Control-Expose-Headers 響應(yīng)標(biāo)頭表明哪些標(biāo)頭可以作為響應(yīng)的一部分公開。默認(rèn)情況下,僅公開6個(gè)CORS安全列出的響應(yīng)標(biāo)頭,分別是

  •  Cache-Control

  •  Content-Language

  •  Content-Type

  •  Expires

  •  Last-Modified

  •  Pragma

如果希望客戶端能夠訪問其他標(biāo)頭,則必須使用 Access-Control-Expose-Headers 標(biāo)頭列出它們。下面是示例

要公開非 CORS 安全列出的請(qǐng)求標(biāo)頭,可以像如下這樣指定

Access-Control-Expose-Headers: Content-Length

要另外公開自定義標(biāo)頭,例如 X-Kuma-Revision,可以指定多個(gè)標(biāo)頭,并用逗號(hào)分隔

Access-Control-Expose-Headers: Content-Length, X-Kuma-Revision

在不是憑證請(qǐng)求中,你還可以使用通配符

Access-Control-Expose-Headers: *

但是,這不會(huì)通配 Authorization 標(biāo)頭,因此如果需要公開它,則需要明確列出

Access-Control-Expose-Headers: *, Authorization

Access-Control-Max-Age

Access-Control-Max-Age 響應(yīng)頭表示預(yù)檢請(qǐng)求的結(jié)果可以緩存多長(zhǎng)時(shí)間,例如

Access-Control-Max-Age: 600

表示預(yù)檢請(qǐng)求可以緩存10分鐘

Access-Control-Request-Headers

瀏覽器在發(fā)出預(yù)檢請(qǐng)求時(shí)使用 Access-Control-Request-Headers 請(qǐng)求標(biāo)頭,使服務(wù)器知道在發(fā)出實(shí)際請(qǐng)求時(shí)客戶端可能發(fā)送的 HTTP 標(biāo)頭。

Access-Control-Request-Headers: X-PINGOTHER, Content-Type

Access-Control-Request-Method

同樣的,Access-Control-Request-Method 響應(yīng)標(biāo)頭告訴服務(wù)器發(fā)出預(yù)檢請(qǐng)求時(shí)將使用那種 HTTP 方法。此標(biāo)頭是必需的,因?yàn)轭A(yù)檢請(qǐng)求始終是 OPTIONS,并且使用的方法與實(shí)際請(qǐng)求不同。

Access-Control-Request-Method: POST

Origin

Origin 請(qǐng)求標(biāo)頭表明匹配的來源,它不包含任何信息,僅僅包含服務(wù)器名稱,它與 CORS 請(qǐng)求以及 POST 請(qǐng)求一起發(fā)送,它類似于 Referer 標(biāo)頭,但與此標(biāo)頭不同,它沒有公開整個(gè)路徑。例如

Origin: https://developer.mozilla.org

HTTP 條件請(qǐng)求

HTTP 具有條件請(qǐng)求的概念,通過比較資源更新生成的值與驗(yàn)證器的值進(jìn)行比較,來確定資源是否進(jìn)行過更新。這樣的請(qǐng)求對(duì)于驗(yàn)證緩存的內(nèi)容、條件請(qǐng)求、驗(yàn)證資源的完整性來說非常重要。

原則

HTTP 條件請(qǐng)求是根據(jù)特定標(biāo)頭的值執(zhí)行不同的請(qǐng)求,這些標(biāo)頭定義了一個(gè)前提條件,如果前提條件匹配或不匹配,則請(qǐng)求的結(jié)果將有所不同。

  •  對(duì)于 安全 的方法,像是 GET、用于請(qǐng)求文檔的資源,僅當(dāng)條件請(qǐng)求的條件滿足時(shí)發(fā)回文檔資源,所以,這種方式可以節(jié)約帶寬。

什么是安全的方法,對(duì)于 HTTP 來說,安全的方法是不會(huì)改變服務(wù)器狀態(tài)的方法,換句話說,如果方法只是只讀操作,那么它肯定是安全的方法,比如說 GET 請(qǐng)求,它肯定是安全的方法,因?yàn)樗皇钦?qǐng)求資源。幾種常見的方法肯定是安全的,它們是 GET、HEAD和 OPTIONS。所有安全的方法都是冪等的(這他媽冪等又是啥意思?)但不是所有冪等的方法都是安全的,例如 PUT 和 DELETE 都是冪等的,但不安全。

冪等性:如果相同的客戶端發(fā)起一次或者多次 HTTP 請(qǐng)求會(huì)得到相同的結(jié)果,則說明 HTTP 是冪等的。(我們這次不深究?jī)绲刃裕?/p>

  •  對(duì)于 非安全 的方法,像是 PUT,只有原始文檔與服務(wù)器上存儲(chǔ)的資源相同時(shí),才可以使用條件請(qǐng)求來傳輸文檔。(PUT 方法通常用來傳輸文件,就像 FTP 協(xié)議的文件上傳一樣)

驗(yàn)證

所有的條件請(qǐng)求都會(huì)嘗試檢查服務(wù)器上存儲(chǔ)的資源是否與某個(gè)特定版本的資源相匹配。為了滿足這種情況,條件請(qǐng)求需要指示資源的版本。由于無法和整個(gè)文件逐個(gè)字符進(jìn)行比較,因此需要把整個(gè)文件描繪成一個(gè)值,然后把此值和服務(wù)器上的資源進(jìn)行比較,這種方式稱為比較器,比較器有兩個(gè)條件

  •  文檔的最后修改日期

  •  一個(gè)不透明的字符串,用于唯一標(biāo)識(shí)每個(gè)版本,稱為實(shí)體標(biāo)簽或 Etag。

比較兩個(gè)資源是否時(shí)相同的版本有些復(fù)雜,根據(jù)上下文,有兩種相等性檢查

  •  當(dāng)期望的是字節(jié)對(duì)字節(jié)進(jìn)行比較時(shí),例如在恢復(fù)下載時(shí),使用強(qiáng) Etag 進(jìn)行驗(yàn)證

  •  當(dāng)用戶代理需要比較兩個(gè)資源是否具有相同的內(nèi)容時(shí),使用若 Etag 進(jìn)行驗(yàn)證

HTTP 協(xié)議默認(rèn)使用 強(qiáng)驗(yàn)證,它指定何時(shí)進(jìn)行弱驗(yàn)證

強(qiáng)驗(yàn)證

強(qiáng)驗(yàn)證保證的是字節(jié) 級(jí)別的驗(yàn)證,嚴(yán)格的驗(yàn)證非常嚴(yán)格,可能在服務(wù)器級(jí)別難以保證,但是它能夠保證任何時(shí)候都不會(huì)丟失數(shù)據(jù),但這種驗(yàn)證丟失性能。

要使用 Last-Modified 很難實(shí)現(xiàn)強(qiáng)驗(yàn)證,通常,這是通過使用帶有資源的 MD5 哈希值的 Etag 來完成的。

弱驗(yàn)證

弱驗(yàn)證不同于強(qiáng)驗(yàn)證,因?yàn)槿绻麅?nèi)容相等,它將認(rèn)為文檔的兩個(gè)版本相同,例如,一個(gè)頁面與另一個(gè)頁面的不同之處僅在于頁腳的日期不同,因此該頁面被認(rèn)為與其他頁面相同。而使用強(qiáng)驗(yàn)證時(shí)則被認(rèn)為這兩個(gè)版本是不同的。構(gòu)建一個(gè)若驗(yàn)證的 Etag 系統(tǒng)可能會(huì)非常復(fù)雜,因?yàn)檫@需要了解每個(gè)頁面元素的重要性,但是對(duì)于優(yōu)化緩存性能非常有用。

下面介紹一下 Etag 如何實(shí)現(xiàn)強(qiáng)弱驗(yàn)證。

Etag 響應(yīng)頭是特定版本的標(biāo)識(shí),它能夠使緩存變得更高效并能夠節(jié)省帶寬,因?yàn)槿绻彺鎯?nèi)容未發(fā)生變更,Web 服務(wù)器則不需要重新發(fā)送完整的響應(yīng)。除此之外,Etag 能夠防止資源同時(shí)更新互相覆蓋。

帶你了解HTTP黑科技

如果給定 URL 上的資源發(fā)生變更,必須生成一個(gè)新的 Etag 值,通過比較它們可以確定資源的兩個(gè)表示形式是否相同。

Etag 值有兩種,一種是強(qiáng) Etag,一種是弱 Etag;

  •  強(qiáng) Etag 值,無論實(shí)體發(fā)生多么細(xì)微的變化都會(huì)改變其值,一般的表示如下 

Etag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
  •  弱 Etag 值,弱 Etag 值只用于提示資源是否相同。只有資源發(fā)生了根本改變,產(chǎn)生差異時(shí)才會(huì)改變 Etag 值。這時(shí),會(huì)在字段值最開始處附加 W/。 

Etag: W/"0815"

下面就來具體探討一下條件請(qǐng)求的標(biāo)頭和 Etag 的關(guān)系

條件請(qǐng)求

條件請(qǐng)求主要包含的標(biāo)頭如下

  •  If-Match

  •  If-None-Match

  •  If-Modified-Since

  •  If-Unmodified-Since

  •  If-Range

If-Match

對(duì)于 GET 和 POST 方法,服務(wù)器僅在與列出的 Etag(響應(yīng)標(biāo)頭) 之一匹配時(shí)才返回請(qǐng)求的資源。這里又多了一個(gè)新詞 Etag,我們稍后再說 Etag 的用法。對(duì)于像是 PUT 和其他非安全的方法,在這種情況下,它僅僅將上傳資源。

下面是兩種常見的案例

  •  對(duì)于 GET 和 POST 方法,會(huì)結(jié)合使用 Range 標(biāo)頭,它可以確保新發(fā)送請(qǐng)求的范圍與上一個(gè)請(qǐng)求的資源相同,如果不匹配的話,會(huì)返回 416 響應(yīng)。

  •  對(duì)于其他方法,特別是 PUT 方法,If-Match 可以防止丟失更新,服務(wù)器會(huì)比對(duì) If-Match 的字段值和資源的 Etag 值,僅當(dāng)兩者一致時(shí),才會(huì)執(zhí)行請(qǐng)求。反之,則返回狀態(tài)碼 412 Precondition Failed 的響應(yīng)。例如 

If-Match: "bfc13a64729c4290ef5b2c2730249c88ca92d82d"  If-Match: *

If-None-Match

條件請(qǐng)求,它與 If-Match 的作用相反,僅當(dāng) If-None-Match 的字段值與 Etag 值不一致時(shí),可處理該請(qǐng)求。對(duì)于GET 和 HEAD ,僅當(dāng)服務(wù)器沒有與給定資源匹配的 Etag 時(shí),服務(wù)器將返回 200 OK作為響應(yīng)。對(duì)于其他方法,僅當(dāng)最終現(xiàn)有資源的 Etag 與列出的任何值都不匹配時(shí),才會(huì)處理請(qǐng)求。

當(dāng) GET 和 POST 發(fā)送的 If-None-Match與 Etag 匹配時(shí),服務(wù)器會(huì)返回 304。

If-None-Match: "bfc13a64729c4290ef5b2c2730249c88ca92d82d"  If-None-Match: W/"67ab43", "54ed21", "7892dd"  If-None-Match: *

If-Modified-Since

If-Modified-Since 是 HTTP 條件請(qǐng)求的一部分,只有在給定日期之后,服務(wù)端修改了請(qǐng)求所需要的資源,才會(huì)返回 200 OK 的響應(yīng)。如果在給定日期之后,服務(wù)端沒有修改內(nèi)容,響應(yīng)會(huì)返回 304 并且不帶任何響應(yīng)體。If-Modified-Since 只能使用 GET 和 HEAD 請(qǐng)求。

If-Modified-Since 與 If-None-Match 結(jié)合使用時(shí),它將被忽略,除非服務(wù)器不支持 If-None-Match。一般表示如下

If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT

注意:這是格林威治標(biāo)準(zhǔn)時(shí)間。 HTTP 日期始終以格林尼治標(biāo)準(zhǔn)時(shí)間表示,而不是本地時(shí)間。

If-Range

If-Range 也是條件請(qǐng)求,如果滿足條件(If-Range 的值和 Etag 值或者更新的日期時(shí)間一致),則會(huì)發(fā)出范圍請(qǐng)求,否則將會(huì)返回全部資源。它的一般表示如下

If-Range: Wed, 21 Oct 2015 07:28:00 GMT   If-Range: bfc13a64729c4290ef5b2c2730249c88ca92d82d

If-Unmodified-Since

If-Unmodified-Since HTTP 請(qǐng)求標(biāo)頭也是一個(gè)條件請(qǐng)求,服務(wù)器只有在給定日期之后沒有對(duì)其進(jìn)行修改時(shí),服務(wù)器才返回請(qǐng)求資源。如果在指定日期時(shí)間后發(fā)生了更新,則以狀態(tài)碼 412 Precondition Failed 作為響應(yīng)返回。

If-Unmodified-Since: Wed, 21 Oct 2015 07:28:00 GMT

條件請(qǐng)求示例

緩存更新

條件請(qǐng)求最常見的示例就是更新緩存,如果緩存是空或沒有緩存,則以200 OK的狀態(tài)發(fā)送回請(qǐng)求的資源。如下圖所示

帶你了解HTTP黑科技

客戶端第一次發(fā)送請(qǐng)求沒有,緩存為空并且沒有條件請(qǐng)求,服務(wù)器在收到客戶端請(qǐng)求后,設(shè)置驗(yàn)證器 Last-Modified 和 Etag 標(biāo)簽,并把這兩個(gè)標(biāo)簽隨著響應(yīng)一起發(fā)送回客戶端。

下一次客戶端再發(fā)送相同的請(qǐng)求后,會(huì)直接從緩存中提取,只要緩存沒有過期,就不會(huì)有任何新的請(qǐng)求到達(dá)服務(wù)器重新下載資源。但是,一旦緩存過期,客戶端不會(huì)直接使用緩存的值,而是發(fā)出條件請(qǐng)求。 驗(yàn)證器的值用作 If-Modified-Since 和 If-Match標(biāo)頭的參數(shù)。

緩存過期后客戶端重新發(fā)起請(qǐng)求,服務(wù)器收到請(qǐng)求后發(fā)現(xiàn)如果資源沒有更改,服務(wù)器會(huì)發(fā)回 304 Not Modified響應(yīng),這使緩存再次刷新,并讓客戶端使用緩存的資源。 盡管有一個(gè)響應(yīng)/請(qǐng)求往返消耗一些資源,但是這比再次通過有線傳輸整個(gè)資源更有效。

帶你了解HTTP黑科技

如果資源已經(jīng)發(fā)生更改,則服務(wù)器僅使用新版本的資源返回 200 OK 響應(yīng),就像沒有條件請(qǐng)求,并且客戶端會(huì)重新使用新的資源,從這個(gè)角度來講,緩存是條件請(qǐng)求的前置條件。

帶你了解HTTP黑科技

斷點(diǎn)續(xù)傳

HTTP 可以支持文件的部分下載,通過保留已獲得的信息,此功能允許恢復(fù)先前的操作,從而節(jié)省帶寬和時(shí)間。

帶你了解HTTP黑科技

支持?jǐn)帱c(diǎn)續(xù)傳的服務(wù)器通過發(fā)送 Accept-Ranges 標(biāo)頭廣播此消息,一旦發(fā)生這種情況,客戶端可以通過發(fā)送缺少范圍的 Ranges 標(biāo)頭來恢復(fù)下載

帶你了解HTTP黑科技

這里你可能有疑問 Ranges 和 Content-Range是什么,來解釋一下

Range

Range HTTP 請(qǐng)求標(biāo)頭指示服務(wù)器應(yīng)返回文檔指定部分的資源,可以一次請(qǐng)求一個(gè) Range 來返回多個(gè)部分,服務(wù)器會(huì)將這些資源返回各個(gè)文檔中。如果服務(wù)器成功返回,那么將返回 206 響應(yīng);如果 Range 范圍無效,服務(wù)器返回416 Range Not Satisfiable錯(cuò)誤;服務(wù)器還可以忽略 Range 標(biāo)頭,并且返回 200 作為響應(yīng)。

Range: bytes=200-1000, 2000-6576, 19000-

還有一種表示是

Range: bytes=0-499, -500

它們分別表示請(qǐng)求前500個(gè)字節(jié)和最后500個(gè)字節(jié),如果范圍重疊,則服務(wù)器可能會(huì)拒絕該請(qǐng)求。

Content-Range

HTTP 的 Content-Range 響應(yīng)標(biāo)頭是針對(duì)范圍請(qǐng)求而設(shè)定的,返回響應(yīng)時(shí)使用首部字段 Content-Range,能夠告知客戶端響應(yīng)實(shí)體的哪部分是符合客戶端請(qǐng)求的,字段以字節(jié)為單位。它的一般表示如下

Content-Range: bytes 200-1000/67589

上段代碼表示從所有 67589 個(gè)字節(jié)中返回 200-1000 個(gè)字節(jié)的內(nèi)容

那么上面的 Content-Range你也應(yīng)該知道是什么意思了

斷點(diǎn)續(xù)傳的原理比較簡(jiǎn)單,但是這種方式存在潛在的問題:如果在兩次下載資源的期間進(jìn)行了資源更新,那么獲得的范圍將對(duì)應(yīng)于資源的兩個(gè)不同版本,并且最終文檔將被破壞。

為了阻止這種情況的出現(xiàn),就會(huì)使用條件請(qǐng)求。對(duì)于范圍來說,有兩種方法可以做到這一點(diǎn)。一種方法是使用 If-Modified-Since和If-Match,如果前提條件失敗,服務(wù)器將返回錯(cuò)誤;然后客戶端從頭開始重新下載。

帶你了解HTTP黑科技

即使此方法有效,當(dāng)文檔資源發(fā)生改變時(shí),它也會(huì)添加額外的 響應(yīng)/請(qǐng)求 交換。這會(huì)降低性能,并且 HTTP 具有特定的標(biāo)頭來避免這種情況 If-Range。

帶你了解HTTP黑科技

該解決方案效率更高,但靈活性稍差一些,因?yàn)樵谶@種情況下只能使用一個(gè) Etag。

通過樂觀鎖避免丟失更新

Web 應(yīng)用程序中最普遍的操作是資源更新。這在任何文件系統(tǒng)或應(yīng)用程序中都很常見,但是任何允許存儲(chǔ)遠(yuǎn)程資源的應(yīng)用程序都需要這種機(jī)制。

使用 put 方法,你可以實(shí)現(xiàn)這一點(diǎn),客戶端首先讀取原始文件對(duì)其進(jìn)行修改,然后把它們發(fā)送到服務(wù)器。

帶你了解HTTP黑科技

上面這種請(qǐng)求響應(yīng)存在問題,一旦考慮到并發(fā)性,事情就會(huì)變得不準(zhǔn)確。當(dāng)客戶端在本地修改資源打算重新發(fā)送之前,第二個(gè)客戶端可以獲取相同的資源并對(duì)資源進(jìn)行修改操作,這樣就會(huì)造成問題。當(dāng)它們重新發(fā)送請(qǐng)求到服務(wù)器時(shí),第一個(gè)客戶端所做的修改將被第二次客戶端的修改所覆蓋,因?yàn)榈诙慰蛻舳诵薷牟⒉恢赖谝淮慰蛻舳苏谛薷摹YY源提交并更新的一方不會(huì)傳達(dá)給另外一方,所以要保留哪個(gè)客戶的更改,將隨著他們提交的速度而變化; 這取決于客戶端,服務(wù)器的性能,甚至取決于人工在客戶端編輯文檔的性能。 例如下面這個(gè)流程

帶你了解HTTP黑科技

如果沒有兩個(gè)用戶同時(shí)操作服務(wù)器,也就不存在這個(gè)問題。但是,現(xiàn)實(shí)情況是不可能只有單個(gè)用戶出現(xiàn)的,所以為了規(guī)避或者避免這個(gè)問題,我們希望客戶端資源在更新時(shí)進(jìn)行提示或者修改被拒絕時(shí)收到通知。

條件請(qǐng)求允許實(shí)現(xiàn)樂觀鎖算法。這個(gè)概念是允許所有的客戶端獲取資源的副本,然后讓他們?cè)诒镜匦薷馁Y源,并成功通過允許第一個(gè)客戶端提交更新來控制并發(fā),基于此服務(wù)端的后面版本的更新都將被拒絕。

帶你了解HTTP黑科技

這是使用 If-Match 或 If-Unmodified-Since標(biāo)頭實(shí)現(xiàn)的。如果 Etag 與原始文件不匹配,或者自獲取以來已對(duì)文件進(jìn)行了修改,則更改為拒絕更新,并顯示412 Precondition Failed錯(cuò)誤。

HTTP Cookies

HTTP 協(xié)議中的 Cookie 包括 Web Cookie 和瀏覽器 Cookie,它是服務(wù)器發(fā)送到 Web 瀏覽器的一小塊數(shù)據(jù)。服務(wù)器發(fā)送到瀏覽器的 Cookie,瀏覽器會(huì)進(jìn)行存儲(chǔ),并與下一個(gè)請(qǐng)求一起發(fā)送到服務(wù)器。通常,它用于判斷兩個(gè)請(qǐng)求是否來自于同一個(gè)瀏覽器,例如用戶保持登錄狀態(tài)。

HTTP Cookie 機(jī)制是 HTTP 協(xié)議無狀態(tài)的一種補(bǔ)充和改良

Cookie 主要用于下面三個(gè)目的

  •  會(huì)話管理

登陸、購物車、游戲得分或者服務(wù)器應(yīng)該記住的其他內(nèi)容

  •  個(gè)性化

用戶偏好、主題或者其他設(shè)置

  •  追蹤

記錄和分析用戶行為

Cookie 曾經(jīng)用于一般的客戶端存儲(chǔ)。雖然這是合法的,因?yàn)樗鼈兪窃诳蛻舳松洗鎯?chǔ)數(shù)據(jù)的唯一方法,但如今建議使用現(xiàn)代存儲(chǔ) API。Cookie 隨每個(gè)請(qǐng)求一起發(fā)送,因此它們可能會(huì)降低性能(尤其是對(duì)于移動(dòng)數(shù)據(jù)連接而言)。客戶端存儲(chǔ)的現(xiàn)代 API 是 Web 存儲(chǔ) API(localStorage 和 sessionStorage)和 IndexedDB。

創(chuàng)建 Cookie

當(dāng)接收到客戶端發(fā)出的 HTTP 請(qǐng)求時(shí),服務(wù)器可以發(fā)送帶有響應(yīng)的 Set-Cookie 標(biāo)頭,Cookie 通常由瀏覽器存儲(chǔ),然后將 Cookie 與 HTTP 標(biāo)頭一同向服務(wù)器發(fā)出請(qǐng)求??梢灾付ǖ狡谌掌诨虺掷m(xù)時(shí)間,之后將不再發(fā)送Cookie。此外,可以設(shè)置對(duì)特定域和路徑的限制,從而限制 cookie 的發(fā)送位置。

Set-Cookie 和 Cookie 標(biāo)頭

Set-Cookie HTTP 響應(yīng)標(biāo)頭將 cookie 從服務(wù)器發(fā)送到用戶代理。下面是一個(gè)發(fā)送 Cookie 的例子

HTTP/2.0 200 OK  Content-type: text/html  Set-Cookie: yummy_cookie=choco  Set-Cookie: tasty_cookie=strawberry  [page content]

此標(biāo)頭告訴客戶端存儲(chǔ) Cookie

現(xiàn)在,隨著對(duì)服務(wù)器的每個(gè)新請(qǐng)求,瀏覽器將使用 Cookie 頭將所有以前存儲(chǔ)的 cookie 發(fā)送回服務(wù)器。

GET /sample_page.html HTTP/2.0  Host: www.example.org  Cookie: yummy_cookie=choco; tasty_cookie=strawberry

Cookie 主要分為三類,它們是 會(huì)話Cookie、永久Cookie 和 Cookie的 Secure 和 HttpOnly 標(biāo)記,下面依次來介紹一下

會(huì)話 Cookies

上面的示例創(chuàng)建的是會(huì)話 Cookie ,會(huì)話 Cookie 有個(gè)特征,客戶端關(guān)閉時(shí) Cookie 會(huì)刪除,因?yàn)樗鼪]有指定Expires 或 Max-Age 指令。 這兩個(gè)指令你看到這里應(yīng)該比較熟悉了。

但是,Web 瀏覽器可能會(huì)使用會(huì)話還原,這會(huì)使大多數(shù)會(huì)話 Cookie 保持永久狀態(tài),就像從未關(guān)閉過瀏覽器一樣

永久性 Cookies

永久性 Cookie 不會(huì)在客戶端關(guān)閉時(shí)過期,而是在特定日期(Expires)或特定時(shí)間長(zhǎng)度(Max-Age)外過期。例如

Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;

Cookie的 Secure 和 HttpOnly 標(biāo)記

安全的 Cookie 需要經(jīng)過 HTTPS 協(xié)議通過加密的方式發(fā)送到服務(wù)器。即使是安全的,也不應(yīng)該將敏感信息存儲(chǔ)在cookie 中,因?yàn)樗鼈儽举|(zhì)上是不安全的,并且此標(biāo)志不能提供真正的保護(hù)。

HttpOnly 的作用

  •  會(huì)話 cookie 中缺少 HttpOnly 屬性會(huì)導(dǎo)致攻擊者可以通過程序(JS腳本、Applet等)獲取到用戶的 cookie 信息,造成用戶cookie 信息泄露,增加攻擊者的跨站腳本攻擊威脅。

  •  HttpOnly 是微軟對(duì) cookie 做的擴(kuò)展,該值指定 cookie 是否可通過客戶端腳本訪問。

  •  如果在 Cookie 中沒有設(shè)置 HttpOnly 屬性為 true,可能導(dǎo)致 Cookie 被竊取。竊取的 Cookie 可以包含標(biāo)識(shí)站點(diǎn)用戶的敏感信息,如 ASP.NET 會(huì)話 ID 或 Forms 身份驗(yàn)證票證,攻擊者可以重播竊取的 Cookie,以便偽裝成用戶或獲取敏感信息,進(jìn)行跨站腳本攻擊等。

Cookie 的作用域

Domain 和 Path 標(biāo)識(shí)定義了 Cookie 的作用域:即 Cookie 應(yīng)該發(fā)送給哪些 URL。

Domain 標(biāo)識(shí)指定了哪些主機(jī)可以接受 Cookie。如果不指定,默認(rèn)為當(dāng)前主機(jī)(不包含子域名)。如果指定了Domain,則一般包含子域名。

感謝各位的閱讀,以上就是“帶你了解HTTP黑科技”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)帶你了解HTTP黑科技這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI