跨域資源共享(CORS)


概念

跨源資源共享 (Cross-origin resource sharing,簡稱CORS) (或通俗地譯為跨域資源共享)是一種基於HTTP頭的機制,該機制通過允許服務器標示除了它自己以外的其它orgin(域,協議和端口),這樣瀏覽器可以訪問加載這些資源。跨源資源共享還通過一種機制來檢查服務器是否會允許要發送的真實請求,該機制通過瀏覽器發起一個到服務器托管的跨源資源的"預檢"請求。在預檢中,瀏覽器發送的頭中標示有HTTP方法和真實請求中會用到的頭。

支持

現代瀏覽器支持在 API 容器中(例如 XMLHttpRequest 或 Fetch )使用 CORS,以降低跨源 HTTP 請求所帶來的風險。

CORS需要瀏覽器和服務器同時支持

  • 整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與(瀏覽器一旦發現請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺)
  • 只要服務器實現了CORS接口,便可以實現跨源通信

目前,所有瀏覽器都支持該功能,IE瀏覽器不能低於IE10。
cors-202109291625064.png

兩種請求

瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。

簡單請求

簡單請求不會觸發 CORS 預檢請求。請注意,該術語並不屬於 Fetch (其中定義了 CORS)規范。

條件

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

  • 使用下列方法之一:
    • GET
    • HEAD
    • POST
  • 除了被用戶代理自動設置的首部字段(例如 Connection ,User-Agent)和在 Fetch 規范中定義為禁用首部名稱 的其他首部,允許人為設置的字段為 Fetch 規范定義的 對 CORS 安全的首部字段集合。該集合為:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (需要注意額外的限制)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • Content-Type 的值僅限於下列三者之一:
    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded
  • 請求中的任意XMLHttpRequestUpload 對象均沒有注冊任何事件監聽器;XMLHttpRequestUpload 對象可以使用 XMLHttpRequest.upload 屬性訪問。
  • 請求中沒有使用 ReadableStream 對象。

特點

對於簡單請求,瀏覽器直接發出CORS請求。具體來說,就是在頭信息之中,增加一個Origin字段。

實例

// 客戶端:http://127.0.0.1:3000
fetch("http://127.0.0.1:4000/simple", {
  method: 'GET',
  headers: {
    'Content-Type': 'text/plain',
  },
}).then(result => {
  return result.text();
}).then(res => {
  console.log('getRequest is succeed:', res);
}).catch(err => {
  console.error('getRequest is faild:', err)
})

// 服務端:http://127.0.0.1:3000
const http = require("http");
http.createServer((request,response)=>{
	if(pathname==='/simple'){
    response.writeHead(200, {
      "Content-Type": "text/plain",
      "Access-Control-Allow-Origin":"http://127.0.0.1:3000"
    })
    response.write("simple request is succeed!");
  }
  response.end();
}).listen(4000);

為了清楚的看到簡單請求和非跨域請求的區別,分別在http://127.0.0.1:3000下以GET方法調用服務地址http://127.0.0.1:3000/simplehttp://127.0.0.1:4000/simple

  • 調用http://127.0.0.1:3000/simple(非跨域請求)

cors-202109291625979.png

  • 調用http://127.0.0.1:4000/simple(簡單請求)

cors-202109291625180.png
對比兩張圖可以發現,簡單請求的響應頭部多了Origin:http//127.0.0.1:3000

非簡單請求

條件

不同時滿足簡單請求條件的,即屬於非簡單請求。非簡單請求會觸發CORS預檢請求。

預檢請求

與前述簡單請求不同,“需預檢的請求”要求必須首先使用 OPTIONS 方法發起一個預檢請求到服務器,以獲知服務器是否允許該實際請求。"預檢請求“的使用,可以避免跨域請求對服務器的用戶數據產生未預期的影響。

實例

// 客戶端:http://127.0.0.1:3000
fetch("http://127.0.0.1:4000/no-simple", {
  method: 'POST',
  body: JSON.stringify({
    name: 'zzcyes',
    birth: '1997'
  }),
  headers: {
    'Content-Type': 'application/json',
  },
  credentials: "include"
}).then(result => {
  return result.text();
}).then(res => {
  console.log('cookie:', document.cookie);
  console.log('getRequest is succeed:', res);
}).catch(err => {
  console.error('getRequest is faild:', err)
})

// 服務端:http://127.0.0.1:4000
// 服務端:http://127.0.0.1:3000
const http = require("http");
http.createServer((request,response)=>{
	if(pathname==='/no-simple'){
    response.writeHead(200, {
      "Content-Type": "text/plain",
      "Access-Control-Allow-Origin":"http://127.0.0.1:3000",
  		"Access-Control-Allow-Headers":"Content-Type,My-Cookie",
      "Access-Control-Allow-Methods":"GET,POST,OPTIONS",
      "Set-Cookie":"name=zzcyes",
    	"Access-Control-Allow-Credentials":true,
      "Access-Control-Max-Age":"10",
    })
    response.write("no simple CORS request is succeed!");
  }
  response.end();
}).listen(4000);

為了清楚的看到簡單請求和非跨域請求的區別,分別在http://127.0.0.1:3000下以POST方法調用服務地址http://127.0.0.1:3000/no-simplehttp://127.0.0.1:4000/no-simple

  • 調用http://127.0.0.1:3000/no-simple(非跨域請求)

cors-202109291625250.png

  • 調用http://127.0.0.1:4000/no-simple(非簡單請求)

預檢請求
cors-202109291625409.png

POST請求(這里請求與響應頭部均與非跨域的POST別無二致)

cors-202109291627460.png

響應首部字段

字段 說明
Access-Control-Allow-Origin 該字段是必須的。它的值要么是請求時Origin字段的值,要么是一個*,表示接受任意域名的請求。
Access-Control-Allow-Methods 該字段必需,它的值是逗號分隔的一個字符串,表明服務器支持的所有跨域請求的方法。注意,返回的是所有支持的方法,而不單是瀏覽器請求的那個方法。這是為了避免多次"預檢"請求。
Access-Control-Expose-Headers 在跨源訪問時,XMLHttpRequest對象的getResponseHeader()方法只能拿到一些最基本的響應頭,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要訪問其他頭,則需要服務器設置本響應頭。
Access-Control-Allow-Headers 如果瀏覽器請求包括Access-Control-Request-Headers字段,則Access-Control-Allow-Headers字段是必需的。它也是一個逗號分隔的字符串,表明服務器支持的所有頭信息字段,不限於瀏覽器在"預檢"中請求的字段。
Access-Control-Allow-Credentials Access-Control-Allow-Credentials 頭指定了當瀏覽器的credentials設置為true時是否允許瀏覽器讀取response的內容。當用在對preflight預檢測請求的響應中時,它指定了實際的請求是否可以使用credentials。請注意:簡單 GET 請求不會被預檢;如果對此類請求的響應中不包含該字段,這個響應將被忽略掉,並且瀏覽器也不會將相應內容返回給網頁。
Access-Control-Max-Age 用來指定本次預檢請求的有效期(秒)

請求首部字段

字段 說明
Origin Origin 首部字段表明預檢請求或實際請求的源站。
Access-Control-Request-Method Access-Control-Request-Method 首部字段用於預檢請求。其作用是,將實際請求所使用的 HTTP 方法告訴服務器。
Access-Control-Request-Headers Access-Control-Request-Headers 首部字段用於預檢請求。其作用是,將實際請求所攜帶的首部字段告訴服務器。

附帶身份憑證的請求

一般而言,對於跨源 XMLHttpRequest 或 Fetch 請求,瀏覽器不會發送身份憑證信息。如果要發送憑證信息,需要設置 XMLHttpRequest的某個特殊標志位。

資源


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM