国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

JavaScript 中的 HTTP 跨域請求

haitiancoder / 1578人閱讀

摘要:而如果跨域請求是從腳本里面發出去的,由于腳本具有高度靈活性,瀏覽器出于安全考慮,會根據同源策略來限制它的功能,使得正常情況下,腳本只能請求同源的資源。反之,則稱為跨域請求,需要遵守機制。非簡單跨域請求在發出請

原文發布于我的博客:https://blog.serenader.me/htt...

自從我接觸前端以來,接手的項目里面很大部分都是前后端分離的,后端只提供接口,前端根據后端接口渲染出實際頁面。個人覺得這是一個挺好的模式,前后端各自負責各自的模塊,分工明確,而且也給前端更大的發揮空間。

與以前套模板的模式不同,前后端分離以后,前端跟后端的溝通絕大部分都是通過前端主動向后端發起請求來完成的。而前端的請求又絕大部分是由 Ajax 構成的,Ajax 是一種非常方便的獲取數據的方式。但是,一旦 Ajax 碰上跨域,那么問題就會麻煩很多。這篇文章主要梳理了我在項目開發里面碰到的一些關于跨域請求的問題,當然也會有一些關于跨域請求的一些背景知識。PS:文末有個小彩蛋哦?

嚴格來說,跨域請求并不僅僅只是 Ajax 的跨域請求,而是對于一個頁面來說,只要它請求了其他域名的資源了,那么這個過程就屬于跨域請求了。比如,一個帶有其他域名的 src 標簽,以及頁面中引入的其他第三方的 CSS 樣式等。

對于 img 以及 CSS 而言,跨域請求本身并沒有更多的安全問題,因為這些請求都屬于只讀請求,并不會對源資源造成副作用。而如果跨域請求是從腳本里面發出去的,由于腳本具有高度靈活性,瀏覽器出于安全考慮,會根據同源策略來限制它的功能,使得正常情況下,腳本只能請求同源的資源。如果頁面確實需要通過腳本請求其他網站的資源,那么就應當在跨域資源共享(CORS)的機制下工作。

等等同學,什么叫做同源策略?

同源策略(Same-origin policy)

對于兩個頁面(資源)而言,只要他們滿足以下三個條件則稱他們符合同源策略:

協議相同

端口相同

域名相同

另外,about:blankjavascript: 繼承加載這些資源的頁面的 origin。data: 的資源不同,自身會擁有一個空的安全的上下文。

另外,子域可以通過JS 設置 document.domain 來通過同源策略。如:

在子域 http://a.example.com/test.html 的頁面中,通過 JS 設置 document.domain="example.com" ,則當前頁面與 http://example.com/page.html 符合同源策略。

簡單的說,對于頁面 http://www.example.com/page1.html 來說,以下頁面與它都不符合同源策略,腳本無法直接請求這些資源:

https://www.example.com/page1.html : 協議不同

http://www.example.com:81/page1.html : 端口不同

http://another.example.com/page1.html : 域名不同

那么,什么又是 CORS 呢?

CORS(Cross-Origin Resource Sharing)

CORS 本質上是規定了一系列的 HTTP 頭來作為判斷腳本是否能夠實現跨域請求。在了解這些請求頭之前,先來看看跨域請求有哪些類型。

通過腳本來發出請求有兩種方式,一種是通過創建 XMLHttpRequest 的方式來發出請求,另外一種是通過 fetch API 來實現請求。

一般來說,跨域請求可以大致分為兩種,其中一種稱之為簡單的請求,其符合以下條件:

請求的方法是 GETPOSTHEAD 其中之一。

除了瀏覽器自動帶上的請求頭(如 Connection User-Agent 等)之外,只允許下面幾種請求:頭

Accept

Accept-Language

Content-Language

Content-Type

Content-Type 請求頭的值只能是 application/x-www-form-urlencodedmultipart/form-datatext/plain 其中之一。

反之,如果有違背上面三條規則中的任意一條,那么即不是簡單的跨域請求。非簡單的跨域請求相對于簡單的跨域請求來說區別在于,請求在發出去之前,瀏覽器會先發送一個 preflighted 請求,用來向服務器端確認接下來要進行的請求是否是被允許的。

Preflight 請求

在實際項目開發中,在使用 XHR 或者 fetch API 請求接口的時候很多情況下都會帶上一些額外的特殊請求頭,或者使用特殊的 HTTP 方法,如 PUTDELETE 等(常見于 Restful 接口)。由于多了額外的請求頭或者使用了特殊的 HTTP 方法,瀏覽器就將這些請求視為非簡單的跨域請求,將會在實際請求發出去之前先自動發出一個 preflight 請求,也就是一個 OPTIONS 請求。

OPTIONS 請求會將當前的跨域請求所使用的特殊 HTTP 請求頭和 HTTP 請求方法發送給服務器端,如 Access-Control-Request-MethodAccess-Control-Request-Headers 。服務器端接收到 OPTIONS 請求后返回相應的響應頭。瀏覽器根據返回的響應頭再來判斷該跨域請求是否被允許的。當瀏覽器判定 OPTIONS 請求通過了,真正的請求才會發出。如以下則是一個帶有 OPTIONS 請求以及真正的 GET 請求的響應頭和請求頭:

OPTIONS /api4 HTTP/1.1
Host: us1.serenader.me:3333
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Access-Control-Request-Method: PUT
Origin: http://us1.serenader.me:3334
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36
Accept: */*
Referer: http://us1.serenader.me:3334/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4,fr;q=0.2
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE
Content-Type: text/html; charset=utf-8
Content-Length: 2
ETag: W/"2-REvLOj/Pg4kpbElGfyfh1g"
Date: Thu, 19 Jan 2017 15:21:15 GMT
Connection: keep-alive
PUT /api4 HTTP/1.1
Host: us1.serenader.me:3333
Connection: keep-alive
Content-Length: 0
Pragma: no-cache
Cache-Control: no-cache
Origin: http://us1.serenader.me:3334
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36
Accept: */*
Referer: http://us1.serenader.me:3334/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4,fr;q=0.2
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: text/html; charset=utf-8
Content-Length: 2
ETag: W/"2-REvLOj/Pg4kpbElGfyfh1g"
Date: Thu, 19 Jan 2017 15:21:15 GMT
Connection: keep-alive

了解了簡單跨域請求以及會發出 preflight 請求的非簡單跨域請求之后,我們再來看看究竟是哪些 HTTP 頭在決定這些跨域請求的「宿命」。

為了幫助讀者更好地理解這些 HTTP 頭的作用,我編寫了一個簡單的 demo ,開源在了 GitHub 上,感興趣的可以到 這個鏈接查看代碼,或者訪問這個在線 demo 預覽效果:http://us1.serenader.me:3334/。記得加載完頁面后打開 Chrome 的控制臺來查看詳細的請求信息。

Access-Control-Allow-Origin

Access-Control-Allow-Origin 是一個響應頭,它指定了當前資源允許被哪些域名的腳本所請求到。

跨域請求(無論簡單請求還是非簡單請求)在發出時都會帶上 Origin 請求頭,用來表明當前發出請求的是哪一個域名。此時服務器端的響應頭里面必須包含一個 Access-Control-Allow-Origin 并且該值匹配 Origin 請求頭,這時候該跨域請求才有可能成功。否則一律失敗。

Access-Control-Allow-Origin 是第一道門檻。其值的匹配規則是:

如果其值是通配符 * 的話,則允許所有的域名進行跨域請求

如果其值是指定的某個固定域名,那么只允許該域名進行跨域請求,其他域名將會失敗

如果其值是帶有通配符的域名,如 *.example.com ,那么則允許該域名以及該域名的子域名進行跨域。

具體可以觀看 demo,demo-0 展示了當腳本請求沒有配置跨域頭的接口時,請求被瀏覽器攔截了的情況:

demo-1 則展示了接口有配置 Access-Control-Allow-Origin 響應頭,但是并非腳本請求的域名,此時瀏覽器會報這種錯:

只有配置了正確的 Access-Control-Allow-Origin 響應頭請求才能夠正常接收到響應,如demo-2,此時的請求頭和響應頭為:

GET /api2 HTTP/1.1
Host: us1.serenader.me:3333
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Origin: http://us1.serenader.me:3334
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36
Accept: */*
Referer: http://us1.serenader.me:3334/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4,fr;q=0.2
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: text/html; charset=utf-8
Content-Length: 2
ETag: W/"2-REvLOj/Pg4kpbElGfyfh1g"
Date: Thu, 19 Jan 2017 15:03:33 GMT
Connection: keep-alive

對于簡單的跨域請求來說,通常只需要通過 Access-Control-Allow-Origin 這個響應頭則可以請求成功(帶 cookie 等情況先不考慮,會在下面討論)。而當請求不是簡單的跨域請求,情況就比較復雜。

Access-Control-Allow-Headers

Access-Control-Allow-Headers 是用來告訴瀏覽器當前接口所允許帶上的特殊請求頭是哪些。這個 HTTP 頭一般會出現在 OPTIONS 請求的響應頭中。

當請求設置了一個特殊的請求頭而且所請求的接口并沒有配置 Access-Control-Allow-Headers 響應頭時,會報如下錯誤,如 demo-3 所示:

上面的截圖展示了請求附帶了一個 X-Custom-Header 的請求頭,但是請求在 preflight 階段就失敗了,如果要讓請求成功完成的話,則必須在 OPTIONS 請求的響應里面配上 Access-Control-Allow-Headers: X-Custom-Header

Access-Control-Allow-Methods

與上一個 HTTP 頭相似,Access-Control-Allow-Methods 告訴瀏覽器當前接口允許使用哪些 HTTP 方法去請求它。這個 HTTP 頭通常也是在 OPTIONS 請求的響應頭中才有意義。當沒有通過這個響應頭時,會報這樣的錯誤:

同樣的,上面的截圖在 preflight 階段就失敗了。如果要讓請求成功執行的話,那么需要配置響應頭為:Access-Control-Allow-Methods: GET,POST,PUT

Access-Control-Max-Age

由于 OPTIONS 請求的存在,對于一個非簡單請求來說,實際發出去的請求會有兩個。這多多少少會浪費帶寬,畢竟這個校驗應該只會在第一次發生而已,一旦通過校驗,在接下來的一段時間里,再次請求該接口的話,那么實際上 OPTIONS 請求則沒有必要再發出。

好在,有個叫做 Access-Control-Max-Age 的響應頭可以實現這樣的功能。這個響應頭指定了請求一旦通過了 preflight 請求之后,會在多長時間內無須再次觸發 preflight 請求。從而達到減少實際請求,減少帶寬浪費的問題。

Access-Control-Allow-Credentials

默認情況下, 任何跨域請求都不會帶上任何身份憑證的,這些身份憑證包括:

cookie

與身份認證相關的請求

TLS 客戶端證書

然而,在大多數情況下,我們需要請求帶上 cookie ,那么則需要開啟跨域請求的 withCredentials 選項。

想要手動開啟傳輸 cookie 的話,有以下方法;

XHR:為 XHR對象設置 xhr.withCredentials = true

fetch: 傳入的參數選項里面開啟 credentials fetch(url, { credentials: "include" })

開啟了 withCredentials 之后,請求在發出去的時候就會默認加上 Cookie。

然而,除了需要在前端中手動開啟 withCredentials 之外,服務器端也需要有相應響應頭支持,請求才會成功。

Access-Control-Allow-Credentials 這個響應頭則是表明了當前請求的資源是否允許附帶身份憑證。當其值為 true 時請求才成功,否則會失敗,失敗內容如下:

可以參考 demo-7觀看請求頭以及響應頭。

另外,一旦開啟了 withCredentials 選項,服務器端的 Access-Control-Allow-Origin 響應頭就不能是通配符,只能是固定的一個域名,否則會請求失敗。具體錯誤內容為:

demo-8 和 demo-9 分別演示了當請求帶上 cookie 時,響應頭配置為通配符的情況以及響應頭有正確配置為具體域名的情況。

總結

總的來說,當在腳本里面發出請求時,會有以下情況:

所請求資源的協議、端口或者域名如果與當前發出請求的頁面地址一致,那么則符合同源策略,請求可以被正常發出。反之,則稱為跨域請求,需要遵守 CORS 機制。

所有跨域請求里面,服務器端必須返回 Access-Control-Allow-Origin 響應頭,并且其值與請求中的 Origin 請求頭的值相匹配。此時請求才可以被允許,否則請求將會被瀏覽器攔截掉。

跨域請求分為兩種,一種是簡單跨域請求,另外一種是非簡單跨域請求。非簡單跨域請求在發出請求之前,瀏覽器會先發出一個 preflight 請求,即一個 OPTIONS 請求,用來驗證服務器端是否允許該請求的訪問。當 OPTIONS 請求成功時,才會繼續發送真正的請求。否則請求將會在 OPTIONS 階段便失敗了,后續真正的請求也不會發出去。

當請求帶上了特殊的請求頭時,服務器端返回的 OPTIONS 請求的響應必須包含 Access-Control-Allow-Headers 響應頭,并且該值包含請求所帶上的特殊請求頭的名稱。這時候請求才會成功,否則會被瀏覽器攔截。

當請求使用了特殊的 HTTP 方法,服務器端返回的 OPTIONS 請求的響應必須包含 Access-Control-Allow-Methods 響應頭,并且該值包含當前使用的 HTTP 方法。如果沒有該響應頭,或者當前使用的方法并不在其值里面,則請求會被瀏覽器攔截。

因為非簡單請求每次完整請求一次資源實際上都會發出去兩個請求,為了減少 OPTIONS 請求發出的次數,以便減少帶寬浪費,服務器端可以配置 Access-Control-Max-Age 來指定瀏覽器可以在多長時間內對 OPTIONS 請求做緩存,使得一次請求成功后,下次請求相同的接口時不用再發出 OPTIONS 請求。

當跨域請求需要帶上 cookie 等身份憑證時,需要手動開啟 withCredentials 選項,并且服務器端需要配置 Access-Control-Allow-Credentials 的響應頭,否則請求將不會帶上任何身份憑證,或者當沒有 Access-Control-Allow-Credentials 時請求會被瀏覽器攔截。

當請求有帶上身份憑證時,服務器端除了需要配置 Access-Control-Allow-Credentials 響應頭之外,Access-Control-Allow-Origin 響應頭的值不能是通配符,必須是具體的某一個域名。否則會被瀏覽器攔截。

在以上 8 點當中,值得注意的是第 3 點和第 8 點。

OPTIONS 請求是一個比較容易被人忽略的一個關鍵點,有一些后端人員在編寫接口的時候,往往只知道在接口的響應頭里面寫入 Access-Control-Allow-Origin ,而沒有意識到 OPTIONS 請求的存在。特別是 OPTIONS 請求并不是每個跨域請求都會帶上的,這就導致了有些人會有疑問,為什么明明我發出去的是 GET 請求,結果卻是發出去了一個 OPTIONS 請求。而即使有對 OPTIONS 請求做跨域允許的話,那么也很容易因為缺少相應的 Access-Control-Allow-HeadersAccess-Control-Allow-Methods 響應頭導致請求仍然失敗。

第 8 點也是一個非常重要的關鍵點。如果你有接口需要對多個不同域名的網站提供服務的話,那么你的接口就不能使用 cookie 等身份憑證了,畢竟 Access-Control-Allow-Origin 不能設置為通配符,限制了接口使用的對象。

彩蛋時間

前面提到了只有非簡單請求才會觸發 OPTIONS 請求,而滿足簡單請求也就只有那三個條件。但是事實并不是想象中的那么完美。

假如你使用了 XMLHttpRequest 來實現文件上傳的話,如果在 xhr.upload 這個對象里面添加任何事件監聽,就會觸發 OPTIONS 請求。即使此時該請求本身是滿足簡單請求的三個條件的。而一旦把事件監聽去掉就沒有。具體可以參考 demo-10、 demo-11、 demo-12

這個「bug」是我當初在編寫 uploader 這個庫時無意間發現的,我當時還以為是瀏覽器的 bug ,但是后來在 Stackoverflow 進行一番搜索后才發現,原來這是瀏覽器隱藏的一個 「feature」。。

Turns out this is not a bug. The spec for XMLHttpRequest does mention that upload progress event handlers should cause the "force preflight" flag to be set. I was a bit confused when this was not specifically mentioned in the CORS spec, even though that spec does reference the existence of a "force preflight" flag.

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81696.html

相關文章

  • JavaScript 的同源策略及其"CORS"跨域方案

    摘要:設置的值,為其當前域或其當前域的父域。場景文檔中的一個腳本執行以下語句即可通過同源檢測跨源網絡訪問同源策略控制了不同源之間的交互。服務器確認允許之后,才發起實際的請求。 文章大綱 同源策略 同源是什么? 如何跨源,以及場景應用 源的更改 跨源網絡訪問 跨源腳本API訪問 跨源數據存儲訪問 了解CORS CORS是什么? CORS功能概述 CORS關于Cookie ...

    maochunguang 評論0 收藏0
  • JavaScript跨域方式

    摘要:跨域原因同源策略在客戶端編程語言中,如和,同源策略是一個很重要的安全理念,它在保證數據的安全性方面有著重要的意義。同源策略規定跨域之間的腳本是隔離的,一個域的腳本不能訪問和操作另外一個域的絕大部分屬性和方法。由兩部分組成回調函數和數據。 1.JavaScript跨域原因--同源策略 在客戶端編程語言中,如javascript和 ActionScript,同源策略是一個很重要的安全理...

    Cciradih 評論0 收藏0
  • javascript跨域

    摘要:實現跨域的原理通過方式請求載入并執行一個文件,相當于通過的形式的導入一個外部的方法語法該函數是簡寫的函數,等價于在中,您可以通過使用形式的回調函數來加載其他網域的數據,如。將自動替換為正確的函數名,以執行回調函數。 更多詳情見http://blog.zhangbing.club/Ja... 最近在項目開發的過程中遇到一些Javascript 跨域請求的問題,今天抽空對其進行總結一下,以...

    PingCAP 評論0 收藏0
  • 大話javascript 5期:跨域

    摘要:同源策略所謂同源是指協議,域名,端口均相同。同源策略是瀏覽器的一個安全功能,不同源的客戶端腳本在沒有明確授權的情況下,不能讀寫對方資源。需注意的是由于同源策略的限制,所讀取的為跨域請求接口所在域的,而非當前頁。 一、什么是跨域 1.URL解析 URL (Uniform Resource Locator )統一資源定位符(URL)是用于完整地描述Internet上網頁和其他資源的地址的...

    jzzlee 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<