跨域資源共享(CORS)
介紹跨域資源共享,一種讓客戶端和服務器通信的方式,即使它們不在同一個域
在瀏覽器中運行的JavaScript應用程序通常只能訪問提供它的相同域名(源)上的HTTP資源。
加載圖片、腳本和樣式始終可以正常工作,但對另一個服務器的XHR和Fetch調用將失敗,除非該服務器實現了一種方法來允許該連接。
這種方法稱為CORS(Cross-Origin Resource Sharing,跨域資源共享)。
默認情況下,使用@font-face
加載Web字體也需要跨域資源共享,還有其他一些不太常見的情況(例如WebGL紋理和Canvas API中加載的drawImage
資源)。
在現代瀏覽器中,使用ES模塊(ES Modules)也需要CORS。
如果您沒有在服務器上設置允許提供第三方域的CORS策略,則請求將失敗。
Fetch示例:
XHR示例:
如果跨域資源違反以下條件,則會失敗:
- 不同的域名
- 不同的子域名
- 不同的端口
- 不同的協議
這樣做是為了您的安全,以防止惡意用戶利用Web平台。
但是,如果您控制服務器和客戶端,則有足夠的理由讓它們互相通信。
如何實現呢?
這取決於您的服務器端堆棧。
瀏覽器支持
非常好(基本上所有瀏覽器,除了IE<10):
使用Express的示例
如果您使用Node.js和Express作為框架,請使用CORS中間件套件。
下面是一個簡單的Express Node.js服務器實現的例子:
1 | const express = require('express') |
如果您從不同的源發起fetch請求並使用中間件函數來處理經過響應的端點請求處理程序,則可以使事情正常工作:
1 | const express = require('express') |
我在Glitch上創建了一個簡單的示例,以下是其代碼: https://glitch.com/edit/#!/flavio-cors-client。
這是Node.js Express服務器: https://glitch.com/edit/#!/flaviocopes-cors-example-express
請注意,在Network面板中,您可以看到由於未正確處理CORS標頭而失敗的請求仍然被接收,您可以在其中找到服務器發送的消息:
只允許特定的域
但是這個例子有個問題:服務器將接受任何請求作為跨域請求。
如您在Network面板中可以看到,通過測試的請求響應標頭中有access-control-allow-origin: *
:
您需要配置服務器以僅允許一個源來提供服務,並阻止其他所有源的服務。
使用相同的cors
Node庫,以下是您應該如何做:
1 | const cors = require('cors') |
您也可以提供多個源:
1 | const whitelist = ['http://example1.com', 'http://example2.com'] |
預檢請求
有一些請求以“簡單”的方式處理,其中包括所有的GET
請求。
還有一些的POST
和HEAD
請求也是如此。
如果POST
請求滿足以下條件中的一個,也屬於這個組別,可以繞過預檢請求:
- 使用以下Content-Type之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
所有其他請求都必須通過預檢請求(preflight)才能進行。瀏覽器通過發送一個OPTIONS
請求來確定是否具有執行操作的權限。
預檢請求包含一些標頭,服務器將使用這些標頭檢查權限(省略的無關字段):
1 | OPTIONS /the/resource/you/request |
服務器將以類似以下的方式響應(省略的無關字段):
1 | HTTP/1.1 200 OK |
我們檢查了POST,但服務器告訴我們,我們還可以對該特定資源發出其他HTTP請求類型。
根據上面的Node.js Express示例,服務器也必須處理OPTIONS請求:
1 | var express = require('express') |
tags: [“CORS”, “跨域資源共享”, “跨域請求”, “網路安全”, “Node.js”, “Express”]