全部產品
Search
文件中心

API Gateway:跨域資源共用(CORS)

更新時間:Jan 04, 2025

跨域帶來的安全問題及瀏覽器的限制訪問

當一個資源從與該資源本身所在的伺服器不同的域或連接埠請求一個資源時,資源會發起一個跨域 HTTP 要求。比如,網站 http://www.aliyun.com 的某 HTML 頁面通過img的src請求http://www.alibaba.com/image.jpg。網路上的許多頁面都會載入來自不同域的CSS樣式表,映像和指令碼等資源。

出於安全原因,瀏覽器限制從頁面指令碼內發起的跨域請求,有些瀏覽器不會限制跨域請求的發起,但是會將結果攔截了。 這意味著使用這些API的Web應用程式只能載入同一個域下的資源,除非使用CORS機制(Cross-Origin Resource Sharing 跨源資源共用)擷取目標伺服器的授權來解決這個問題。

1

上圖展示的是典型的跨域情境,目前主流瀏覽器為了使用者的安全,都會預設禁止跨域訪問,但是主流瀏覽器都支援W3C推薦的一種跨域資源共用機制(CORS)。伺服器端配合瀏覽器實現CORS機制,可以突破瀏覽器對跨域資源訪問的限制,實現跨域資源請求。

2

跨域資源共用CORS介紹

兩種驗證模式

跨域資源共用CORS的驗證機制分兩種模式:簡單請求和預先請求。

當請求同時滿足下面三個條件時,CORS驗證機制會使用簡單模式進行處理。否則CORS驗證機制會使用預先請求模式進行處理。

1.要求方法是下列之一:

  • GET

  • HEAD

  • POST

2.要求標頭中的Content-Type要求標頭的值是下列之一:

  • application/x-www-form-urlencoded

  • multipart/form-data

  • text/plain

3.Fetch規範定義了CORS安全頭的集合(跨域請求中自訂的頭屬於安全頭的集合)。該集合為:

  • Accept

  • Accept-Language

  • Content-Language

  • Content-Type (需要注意額外的限制)

  • DPR

  • Downlink

  • Save-Data

  • Viewport-Width

  • Width

1. 簡單請求模式

簡單請求模式,瀏覽器直接發送跨域請求,並在要求標頭中攜帶Origin的頭,表明這是一個跨域的請求。 伺服器端接到請求後,會根據自己的跨域規則,通過Access-Control-Allow-Origin和Access-Control-Allow-Methods回應標頭,來返回驗證結果。

3

應答中攜帶了跨域頭 Access-Control-Allow-Origin。使用 Origin 和 Access-Control-Allow-Origin 就能完成最簡單的存取控制。本例中,服務端返回的 Access-Control-Allow-Origin: * 表明,該資源可以被任意外域訪問。如果服務端僅允許來自 http://www.aliyun.com 的訪問,該首部欄位的內容如下:

Access-Control-Allow-Origin: http://www.aliyun.com

現在,除了 http://www.aliyun.com ,其他外域均不能訪問該資源。

2. 預先請求模式

瀏覽器在發現頁面發出的請求非簡單請求,並不會立即執行對應的請求代碼,而是會觸發預先請求模式。預先請求模式會先發送Preflighted requests(預先驗證請求),Preflighted requests是一個OPTIONS請求,用於詢問要被跨域訪問的伺服器,是否允許當前網域名稱下的頁面發送跨域的請求。在得到伺服器的跨域授權後才能發送真正的HTTP請求。

OPTIONS要求標頭部中會包含以下頭部:Origin、Access-Control-Request-Method、Access-Control-Request-Headers。 伺服器收到OPTIONS請求後,設定Access-Control-Allow-Origin、Access-Control-Allow-Method、Access-Control-Allow-Headers、Access-Control-Max-Age頭部與瀏覽器溝通來判斷是否允許這個請求。 如果Preflighted requests驗證通過,瀏覽器才會發送真正的跨域請求。

4

請求中的跨域頭 Access-Control-Request-Method 告知伺服器,實際請求將使用 GET 方法。 請求中的跨域頭 Access-Control-Request-Headers 告知伺服器,實際請求將攜帶兩個自訂請求首部欄位:x-ca-nonce 與 content-type。伺服器據此決定該實際請求是否被允許。

  • 應答中的跨域頭 Access-Control-Allow-Methods 表明伺服器允許用戶端使用 GET 方法發起請求。值為逗號分割的列表。

  • 應答中的跨域頭 Access-Control-Allow-Headers 表明伺服器允許請求中攜帶欄位 x-ca-nonce 與 content-type。與 Access-Control-Allow-Methods 一樣,Access-Control-Allow-Headers 的值為逗號分割的列表。

  • 應答中的跨域頭 Access-Control-Max-Age 表明該響應的有效時間為 86400 秒,也就是 24 小時。在有效時間內,瀏覽器無須為同一請求再次發起預檢請求。請注意,瀏覽器自身維護了一個最大有效時間,如果該首部欄位的值超過了最大有效時間,將不會生效。

在API Gateway實現CORS跨域資源共用

1. 實現簡單請求模式

API Gateway預設所有API允許跨域訪問,因此如果使用者的API後端服務的應答中不做特殊返回,API Gateway會返回允許所有域跨域訪問的相關頭,下面是一個樣本:

用戶端的API請求:

GET /simple HTTP/1.1
Host: www.alibaba.com
origin: http://www.aliyun.com
content-type: application/x-www-form-urlencoded; charset=utf-8
accept: application/json; charset=utf-8
date: Mon, 18 Sep 2017 09:53:23 GMT                

後端服務應答:

HTTP/1.1 200 OK
Date: Mon, 18 Sep 2017 09:53:23 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 12
{"200","OK"}                

API Gateway應答:

HTTP/1.1 200 OK
Date: Mon, 18 Sep 2017 09:53:23 GMT
Access-Control-Allow-Origin: *
X-Ca-Request-Id: 104735BD-8968-458F-9929-DBFA43F324C6
Content-Type: application/json; charset=UTF-8
Content-Length: 12
{"200","OK"}        

從上面三個報文可以看出,API Gateway會對使用者的後端服務應答做一定修改,增加一個跨域頭:

Access-Control-Allow-Origin: *        

這個跨域頭的意思是,本API允許所有域的請求訪問。

如果使用者需要定製針對簡單請求的應答的跨域頭,只需要在後端服務應答中,增加Access-Control-Allow-Origin這個跨域頭即可,後端服務應答中的頭會預設覆蓋掉API Gateway自己增加的頭。下面是一個例子,這個例子中的API只允許http://www.aliyun.com 這一個域訪問。

用戶端的API請求:

GET /simple HTTP/1.1
Host: www.alibaba.com
origin: http://www.aliyun.com
content-type: application/x-www-form-urlencoded; charset=utf-8
accept: application/json; charset=utf-8
date: Mon, 18 Sep 2017 09:53:23 GMT        

後端服務應答:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.aliyun.com 
Date: Mon, 18 Sep 2017 09:53:23 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 12
{"200","OK"}

API Gateway應答:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.aliyun.com 
X-Ca-Request-Id: 104735BD-8968-458F-9929-DBFA43F324C6
Date: Mon, 18 Sep 2017 09:53:23 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 12
{"200","OK"}

2. 實現預先請求模式

API Gateway允許使用者佈建方法為OPTIONS的API,並且將後端服務的OPTIONS應答透傳給用戶端。建立方法為OPTIONS的API,定義的其他部分與正常API一樣,有兩點需要注意:

  • 定義API認證方式時選擇無認證。

  • 定義API請求時,需要設定path為/,並且匹配所有子路徑。選定方法為OPTIONS,API Gateway控制台會預設佈建要求模式為透傳模式,且不可修改,使用者不需要定義請求參數。

使用者可以在每個API分組下建立一個方法的OPTIONS的API,來定義這一組API綁定的網域名稱的跨域資源策略。 使用者可以使用curl方法來測試自己的跨域API應答情況,下面是針對一個定義好的OPTIONS的API訪問的樣本。

sudo curl -X OPTIONS -H "Access-Control-Request-Method:POST" -H "Access-Control-Request-Headers:X-CUSTOM-HEADER" http://ec12ac094e734544be02c928366b7b26-cn-qingdao.alicloudapi.com/optinstest -i
HTTP/1.1 200 OK
Server: Tengine
Date: Sun, 02 Sep 2018 15:32:19 GMT
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
Access-Control-Allow-Headers: X-CUSTOM-HEADER
Access-Control-Max-Age: 172800
X-Ca-Request-Id: 1016AC86-E345-405C-8049-A6C24078F65F
                        

使用者在實現方法為OPTIONS的API的時候需要注意的一點是: API Gateway會對使用者的後端服務應答做一定修改,增加四個跨域頭(Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers、Access-Control-Max-Age),後端服務應答中,需要返回所有跨域頭來覆蓋API Gateway預設跨域頭。

下面是一個完整的預先請求模式的請求與應答樣本。

用戶端的方法為OPTIONS的API請求:

OPTIONS /simple HTTP/1.1
Host: www.alibaba.com
origin: http://www.aliyun.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
accept: application/json; charset=utf-8
date: Mon, 18 Sep 2017 09:53:23 GMT

後端服務應答:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.aliyun.com 
Access-Control-Allow-Methods: GET,POST
Access-Control-Allow-Headers: X-CUSTOM-HEADER
Access-Control-Max-Age: 10000
Date: Mon, 18 Sep 2017 09:53:23 GMT
Content-Type: application/json; charset=UTF-8

API Gateway應答:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.aliyun.com 
Access-Control-Allow-Methods: GET,POST
Access-Control-Allow-Headers: X-CUSTOM-HEADER
Access-Control-Max-Age: 10000
X-Ca-Request-Id: 104735BD-8968-458F-9929-DBFA43F324C6
Date: Mon, 18 Sep 2017 09:53:23 GMT
Content-Type: application/json; charset=UTF-8

用戶端發送正常業務請求:

GET /simple HTTP/1.1
Host: www.alibaba.com
origin: http://www.aliyun.com
content-type: application/x-www-form-urlencoded; charset=utf-8
accept: application/json; charset=utf-8
date: Mon, 18 Sep 2017 09:53:23 GMT

後端服務應答:

HTTP/1.1 200 OK
Date: Mon, 18 Sep 2017 09:53:23 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 12
{"200","OK"}

API Gateway應答:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,HEAD,OPTIONS,PATCH
Access-Control-Allow-Headers: X-Requested-With,X-Sequence,X-Ca-Key,X-Ca-Secret,X-Ca-Version,X-Ca-Timestamp,X-Ca-Nonce,X-Ca-API-Key,X-Ca-Stage,X-Ca-Client-DeviceId,X-Ca-Client-AppId,X-Ca-Signature,X-Ca-Signature-Headers,X-Forwarded-For,X-Ca-Date,X-Ca-Request-Mode,Authorization,Content-Type,Accept,Accept-Ranges,Cache-Control,Range,Content-MD5
Access-Control-Max-Age: 172800
X-Ca-Request-Id: 104735BD-8968-458F-9929-DBFA43F324C6
Date: Mon, 18 Sep 2017 09:53:23 GMT
Content-Type: application/json; charset=UTF-8
Content-Length: 12
{"200","OK"}