CORS講解

来源:https://www.cnblogs.com/Vincent-yuan/archive/2019/05/08/10835094.html
-Advertisement-
Play Games

跨域資源共用(CORS) 是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器 讓運行在一個 origin (domain) 上的Web應用被准許訪問來自不同源伺服器上的指定的資源。當一個資源從與該資源本身所在的伺服器不同的域、協議或埠請求一個資源時,資源會發起一個跨域 HTTP 請求。 什麼情況下 ...


 

跨域資源共用(CORS) 是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器  讓運行在一個 origin (domain) 上的Web應用被准許訪問來自不同源伺服器上的指定的資源。當一個資源從與該資源本身所在的伺服器不同的域、協議或埠請求一個資源時,資源會發起一個跨域 HTTP 請求

 

 

什麼情況下需要 CORS ?

 

功能概述

跨域資源共用標準新增了一組 HTTP 首部欄位,允許伺服器聲明哪些源站通過瀏覽器有許可權訪問哪些資源。

另外,規範要求,對那些可能對伺服器數據產生副作用的 HTTP 請求方法(特別是 GET 以外的 HTTP 請求,或者搭配某些 MIME 類型的 POST 請求),

瀏覽器必須首先使用 OPTIONS 方法發起一個預檢請求(preflight request),從而獲知服務端是否允許該跨域請求。

伺服器確認允許之後,才發起實際的 HTTP 請求。

在預檢請求的返回中,伺服器端也可以通知客戶端,是否需要攜帶身份憑證(包括 Cookies 和 HTTP 認證相關數據)。

 

若幹訪問控制場景

簡單請求

某些請求不會觸發 CORS 預檢請求。本文稱這樣的請求為“簡單請求”。

若請求滿足所有下述條件,則該請求可視為“簡單請求”:

 

註意: 這些跨域請求與瀏覽器發出的其他跨域請求並無二致。如果伺服器未返回正確的響應首部,則請求方不會收到任何數據。因此,那些不允許跨域請求的網站無需為這一新的 HTTP 訪問控制特性擔心。

 

比如說,假如站點 http://foo.example 的網頁應用想要訪問 http://bar.other 的資源。http://foo.example 的網頁中可能包含類似於下麵的 JavaScript 代碼:

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

客戶端和伺服器之間使用 CORS 首部欄位來處理跨域許可權:

 

 分別檢視請求報文和響應報文:

 1 GET /resources/public-data/ HTTP/1.1
 2 Host: bar.other
 3 User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
 4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
 5 Accept-Language: en-us,en;q=0.5
 6 Accept-Encoding: gzip,deflate
 7 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
 8 Connection: keep-alive
 9 Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
10 Origin: http://foo.example
11 
12 
13 HTTP/1.1 200 OK
14 Date: Mon, 01 Dec 2008 00:23:53 GMT
15 Server: Apache/2.0.61 
16 Access-Control-Allow-Origin: *
17 Keep-Alive: timeout=2, max=100
18 Connection: Keep-Alive
19 Transfer-Encoding: chunked
20 Content-Type: application/xml
21 
22 [XML Data]

第 1~10 行是請求首部。第10行 的請求首部欄位 Origin 表明該請求來源於 http://foo.exmaple

第 13~22 行是來自於 http://bar.other 的服務端響應。響應中攜帶了響應首部欄位 Access-Control-Allow-Origin(第 16 行)。

使用 Origin 和 Access-Control-Allow-Origin 就能完成最簡單的訪問控制。

本例中,服務端返回的 Access-Control-Allow-Origin: * 表明,該資源可以被任意外域訪問。

 

如果服務端僅允許來自 http://foo.example 的訪問,該首部欄位的內容如下:

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

現在,除了 http://foo.example,其它外域均不能訪問該資源(該策略由請求首部中的 ORIGIN 欄位定義,見第10行)。

Access-Control-Allow-Origin 應當為 * 或者包含由 Origin 首部欄位所指明的功能變數名稱。

 

預檢請求

 與前述簡單請求不同,“需預檢的請求”要求必須首先使用 OPTIONS   方法發起一個預檢請求到伺服器,以獲知伺服器是否允許該實際請求。

"預檢請求“的使用,可以避免跨域請求對伺服器的用戶數據產生未預期的影響

請求滿足下述任一條件時,即應首先發送預檢請求:

 

如下是一個需要執行預檢請求的 HTTP 請求:

 1 var invocation = new XMLHttpRequest();
 2 var url = 'http://bar.other/resources/post-here/';
 3 var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
 4     
 5 function callOtherDomain(){
 6   if(invocation)
 7     {
 8       invocation.open('POST', url, true);
 9       invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
10       invocation.setRequestHeader('Content-Type', 'application/xml');
11       invocation.onreadystatechange = handler;
12       invocation.send(body); 
13     }
14 }
15 
16 ......

上面的代碼使用 POST 請求發送一個 XML 文檔,該請求包含了一個自定義的請求首部欄位(X-PINGOTHER: pingpong)。

另外,該請求的 Content-Type 為 application/xml。因此,該請求需要首先發起“預檢請求”。

 

 

 

 1.OPTIONS /resources/post-here/ HTTP/1.1
 2.Host: bar.other
 3.User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
 4.Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
 5.Accept-Language: en-us,en;q=0.5
 6.Accept-Encoding: gzip,deflate
 7.Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
 8.Connection: keep-alive
 9.Origin: http://foo.example
10.Access-Control-Request-Method: POST
11.Access-Control-Request-Headers: X-PINGOTHER, Content-Type
12.
13.
14.HTTP/1.1 200 OK
15.Date: Mon, 01 Dec 2008 01:15:39 GMT
16.Server: Apache/2.0.61 (Unix)
17.Access-Control-Allow-Origin: http://foo.example
18.Access-Control-Allow-Methods: POST, GET, OPTIONS
19.Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
20.Access-Control-Max-Age: 86400
21.Vary: Accept-Encoding, Origin
22.Content-Encoding: gzip
23.Content-Length: 0
24.Keep-Alive: timeout=2, max=100
25.Connection: Keep-Alive
26.Content-Type: text/plain

預檢請求完成之後,發送實際請求:

POST /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
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
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache

<?xml version="1.0"?><person><name>Arun</name></person>


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://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]

瀏覽器檢測到,從 JavaScript 中發起的請求需要被預檢。從上面的報文中,我們看到,第 1~12 行發送了一個使用 OPTIONS 方法的“預檢請求”。

 OPTIONS 是 HTTP/1.1 協議中定義的方法,用以從伺服器獲取更多信息。

該方法不會對伺服器資源產生影響。 預檢請求中同時攜帶了下麵兩個首部欄位:

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

首部欄位 Access-Control-Request-Method 告知伺服器,實際請求將使用 POST 方法。

首部欄位 Access-Control-Request-Headers 告知伺服器,實際請求將攜帶兩個自定義請求首部欄位:X-PINGOTHER 與 Content-Type。

伺服器據此決定,該實際請求是否被允許。

 

第14~26 行為預檢請求的響應,表明伺服器將接受後續的實際請求。重點看第 17~20 行:

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

首部欄位 Access-Control-Allow-Methods 表明伺服器允許客戶端使用 POST, GET 和 OPTIONS 方法發起請求。

首部欄位 Access-Control-Allow-Headers 表明伺服器允許請求中攜帶欄位 X-PINGOTHER Content-Type

最後,首部欄位 Access-Control-Max-Age 表明該響應的有效時間為 86400 秒,也就是 24 小時。在有效時間內,瀏覽器無須為同一請求再次發起預檢請求。

 

預檢請求與重定向

大多數瀏覽器不支持針對於預檢請求的重定向。如果一個預檢請求發生了重定向,瀏覽器將報告錯誤:

The request was redirected to 'https://example.com/foo', which is disallowed for cross-origin requests that require preflight
Request requires preflight, which is disallowed to follow cross-origin redirect

在瀏覽器的實現跟上規範之前,有兩種方式規避上述報錯行為:

  • 在服務端去掉對預檢請求的重定向;
  • 將實際請求變成一個簡單請求。

如果上面兩種方式難以做到,我們仍有其他辦法:

不過,如果請求是由於存在 Authorization 欄位而引發了預檢請求,則這一方法將無法使用。這種情況只能由服務端進行更改

 

附帶身份憑證的請求

 Fetch 與 CORS 的一個有趣的特性是,可以基於  HTTP cookies 和 HTTP 認證信息發送身份憑證。

一般而言,對於跨域 XMLHttpRequest 或 Fetch 請求,瀏覽器不會發送身份憑證信息。

如果要發送憑證信息,需要設置 XMLHttpRequest 的某個特殊標誌位。

 

本例中,http://foo.example 的某腳本向 http://bar.other 發起一個GET 請求,並設置 Cookies:

var invocation = new XMLHttpRequest();
var 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 的 withCredentials 標誌設置為 true,從而向伺服器發送 Cookies。

因為這是一個簡單 GET 請求,所以瀏覽器不會對其發起“預檢請求”。

但是,如果伺服器端的響應中未攜帶 Access-Control-Allow-Credentials: true ,瀏覽器將不會把響應內容返回給請求的發送者。

客戶端與伺服器端交互示例如下:

 1 GET /resources/access-control-with-credentials/ HTTP/1.1
 2 Host: bar.other
 3 User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
 4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
 5 Accept-Language: en-us,en;q=0.5
 6 Accept-Encoding: gzip,deflate
 7 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
 8 Connection: keep-alive
 9 Referer: http://foo.example/examples/credential.html
10 Origin: http://foo.example
11 Cookie: pageAccess=2
12 
13 
14 HTTP/1.1 200 OK
15 Date: Mon, 01 Dec 2008 01:34:52 GMT
16 Server: Apache/2.0.61 (Unix) PHP/4.4.7 mod_ssl/2.0.61 OpenSSL/0.9.7e mod_fastcgi/2.4.2 DAV/2 SVN/1.4.2
17 X-Powered-By: PHP/5.2.6
18 Access-Control-Allow-Origin: http://foo.example
19 Access-Control-Allow-Credentials: true
20 Cache-Control: no-cache
21 Pragma: no-cache
22 Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
23 Vary: Accept-Encoding, Origin
24 Content-Encoding: gzip
25 Content-Length: 106
26 Keep-Alive: timeout=2, max=100
27 Connection: Keep-Alive
28 Content-Type: text/plain
29 
30 
31 [text/plain payload]

即使第 11 行指定了 Cookie 的相關信息,但是,如果 bar.other 的響應中缺失 Access-Control-Allow-Credentials: true(第 19 行),則響應內容不會返回給請求的發起者。

 

附帶身份憑證的請求與通配符

對於附帶身份憑證的請求,伺服器不得設置 Access-Control-Allow-Origin 的值為“*”。

這是因為請求的首部中攜帶了 Cookie 信息,如果 Access-Control-Allow-Origin 的值為“*”,請求將會失敗。

而將 Access-Control-Allow-Origin 的值設置為 http://foo.example,則請求將成功執行。

 

另外,響應首部中也攜帶了 Set-Cookie 欄位,嘗試對 Cookie 進行修改。如果操作失敗,將會拋出異常。

 

HTTP 響應首部欄位

Access-Control-Allow-Origin

Access-Control-Allow-Origin: <origin> | *

 

其中,origin 參數的值指定了允許訪問該資源的外域 URI。對於不需要攜帶身份憑證的請求,伺服器可以指定該欄位的值為通配符,表示允許來自所有域的請求。

 

Access-Control-Expose-Headers

在跨域訪問時,XMLHttpRequest對象的getResponseHeader()方法只能拿到一些最基本的響應頭,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要訪問其他頭,則需要伺服器設置本響應頭。

Access-Control-Expose-Headers 頭讓伺服器把允許瀏覽器訪問的頭放入白名單,例如:

Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header

這樣瀏覽器就能夠通過getResponseHeader訪問X-My-Custom-Header和 X-Another-Custom-Header 響應頭了

 

Access-Control-Max-Age

Access-Control-Max-Age 頭指定了preflight請求的結果能夠被緩存多久

Access-Control-Max-Age: <delta-seconds>

delta-seconds 參數表示preflight請求的結果在多少秒內有效。

 

Access-Control-Allow-Credentials

Access-Control-Allow-Credentials 頭指定了當瀏覽器的credentials設置為true時是否允許瀏覽器讀取response的內容。

當用在對preflight預檢測請求的響應中時,它指定了實際的請求是否可以使用credentials

請註意:簡單 GET 請求不會被預檢;如果對此類請求的響應中不包含該欄位,這個響應將被忽略掉,並且瀏覽器也不會將相應內容返回給網頁。

 

Access-Control-Allow-Credentials: true

 

Access-Control-Allow-Methods

Access-Control-Allow-Methods 首部欄位用於預檢請求的響應。其指明瞭實際請求所允許使用的 HTTP 方法。

Access-Control-Allow-Methods: <method>[, <method>]*

 

Access-Control-Allow-Headers

Access-Control-Allow-Headers 首部欄位用於預檢請求的響應。其指明瞭實際請求中允許攜帶的首部欄位。

Access-Control-Allow-Headers: <field-name>[, <field-name>]*

 

HTTP 請求首部欄位

 

做記錄

參考網址

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • pip: 一個現代的,通用的 Python 包管理工具。提供了對Python 包的查找、下載、安裝、卸載的功能。 windows:自帶pip,直接使用。 Linux:執行下麵命令即可完成安裝。 Usage: pip <command> [options] 查看版本:pip show package_ ...
  • 1.介面定義 介面屬於一個特殊的類,這個類裡面只能有抽象方法和全局常量 (該概念在JDK1.8之後被打破,在1.8後介面中還可以定義普通方法和靜態方法,在後續章節會詳講) 1.1 介面具有以下幾個原則 介面通過interface關鍵字來實現定義 一個子類如果要繼承介面的話,則需要通過implemen ...
  • 本次內容是基於獨立的“企業微信”而言的; 企業微信有獨立的管理後臺,和一般的服務號,訂閱號後臺不同; 企業微信涉及員工,所以這裡的支付是付款到員工零錢; 官方API文檔: https://work.weixin.qq.com/api/doc#90000/90135/90278 下麵具體步驟: 1:先 ...
  • CodeFirst的開發模式,資料庫的設計細節完全靠代碼來完成,數據實體映射配置正是負責這項工作的,針對一個實體,可以在這裡配置其在資料庫中的數據表關係、數據約束及各個數據欄位的每一個細節配置 ...
  • 作為上床後需要下床檢查好幾次門關了沒有的資深強迫症患者,有一個及其搞我的問題,就是dll問題。 曾幾何時,在沒有nuget的年代,當有依賴項需要引用的時候,只能通過文件引用來管理引用問題,版本問題,更新問題層出不窮,很是難受。 後來出來nuget,喜大普奔,總算解決了引用的問題。開心之餘,依然還有一 ...
  • 你將要創造什麼 Unity是由Unity Technologies開發的多平臺游戲引擎,用於為控制台,移動設備,電腦甚至網站等多種設備創建視頻游戲和應用程式。Unity的核心優勢在於其穩健性,可移植性和社區性; Unity針對幾個眾所周知的API,如Direct3D,OpenGL,Op​​enGL ...
  • sqlserver使用EF模型經驗 EF模型使用本人在之前兩三年中從沒使用過,所以剛開始使用就會踩上許多的坑。今天我不單單說下自己所踩的一些坑與當前公司中使用EF模型設計的理念,即是為我自己做個筆記,也是為其他人提供一個經驗吧,但本人剛接觸不久,有理解錯誤的還請大家能夠多多指教。 首先說一下我現在所 ...
  • 最近每天在用VS2017,但是每次打開它都會彈出最近項目的記錄,很是煩人。 最主要是我不想別人得知我最近的項目和項目進度,每次加密項目會比較麻煩。 所以經過簡單的研究,編寫了這個小工具,打開直接單擊就可以清楚最近項目的記錄。 算是一種隱私安全小工具吧! 有問題或者任何建議,歡迎騷擾!工具會持續更新優 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...