跨域資源共享CORS

參考: HTTP訪問控制(CORS)

當頁面與頁面請求的資源 不在 同一域/協議/端口 時,會發起一個 跨域HTTP請求php

出於安全緣由,瀏覽器會限制從腳本內發起的跨源HTTP請求或響應。html

經過跨域資源共享 CORS(cross-origin share standard) 機制,可以使跨域數據傳輸安全進行。canvas

1.跨域的請求類型

HTTP請求頭部字段跨域

頭部 名稱 說明
Origin 源站域名 XMLHttpRequest跨域才發送
Image需標明 crossOrigin=anonymous/use-credentials
有些服務經過判斷有沒有Origin來決定是否展現受權相關的響應頭部字段,例如阿里雲OSS
Access-Control-Request-Method 實際請求的 HTTP 方法
Access-Control-Request-Headers 實際請求的攜帶的自定義頭部字段名字

HTTP響應頭部字段瀏覽器

頭部 名稱 說明
Access-Control-Allow-Origin 容許訪問該資源的URI *http://example.com; 若爲附帶身份憑證的請求,此處不能爲 *
Access-Control-Allow-Credentials 是否容許附帶身份憑證的請求 true; 若容許則請求與響應資源互通cookie
Access-Control-Expose-Headers 容許客戶端讀取的響應頭部 x-server-one,x-server-two
Access-Control-Max-Age 預檢請求的結果緩存多少秒 3600; 緩存時間內不進行 options 請求
Access-Control-Allow-Methods 預檢請求:實際請求容許的 HTTP 方法
Access-Control-Allow-Headers 預檢請求:實際請求容許的自定義頭部

1.簡單請求

非預檢請求都是簡單請求。緩存

2.預檢請求

當請求知足下述任一條件時,將首先使用 OPTIONS 方法發起一個預檢請求到服務器,以獲知服務器是否容許該實際請求。安全

  • 使用了下面任一 HTTP 方法: PUT / DELETE / CONNECT / OPTIONS / TRACE / PATCH
  • 人爲設置了對 CORS 安全的首部字段集合以外的其餘首部字段。該集合爲:Accept / Accept-Language / Content-Language / Content-Type / DPR / Downlink / Save-Data / Viewport-Width / Width
  • Content-Type 的值不屬於下列之一: application/x-www-form-urlencoded / multipart/form-data / text/plain

3.附帶身份憑證的請求

2.可以使用CORS的場景

1.XMLHttpRequest 或 Fetch 發起的跨域 HTTP 請求

例1:domain1.comrequest.js 請求 domain2.com 上的 1.php服務器

// request.js
const req = new XMLHttpRequest();
// 跨域:須要返回響應頭 Access-Control-Allow-Origin: *
req.open('POST', 'https://domain2.com/1.php', true);
// 客戶端自定義請求頭:須要返回響應頭 Access-Control-Expose-Headers: X-Client-One,X-Client-Two
req.setRequestHeader('X-Client-One', 'pingpong1');
req.setRequestHeader('X-Client-Two', 'pingpong2');
// 須要附帶身份憑證:須要返回響應頭 Access-Control-Allow-Credentials: true
req.withCredentials = true;
req.onreadystatechange = () => {
  // 請求已完成,且響應已就緒
  if (req.readyState === 4) {
    console.log(req.getResponseHeader('x-server-one'));
    console.log(req.getResponseHeader('x-server-two'));
    console.log(req.response);
  }
};
req.send();

過程cookie

# 請求1頭
Origin: https://domain1.com
Access-Control-Request-Headers: x-client-one,x-client-two
Access-Control-Request-Method: POST

# 響應1頭
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: X-Client-One,X-Client-Two
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,PATCH,OPTIONS
Access-Control-Allow-Origin: https://domain1.com
Access-Control-Expose-Headers: x-server-one,x-server-two
x-server-one: hello
x-server-two: hihi

# 請求2頭
Origin: https://domain1.com
X-Client-One: pingpong1
X-Client-Two: pingpong2

# 響應2頭
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: X-Client-One,X-Client-Two
Access-Control-Allow-Methods: GET,POST,PUT,DELETE,PATCH,OPTIONS
Access-Control-Allow-Origin: https://domain1.com
Access-Control-Expose-Headers: x-server-one,x-server-two
x-server-one: hello
x-server-two: hihi

2.CSS 中經過 @font-face 使用跨域字體資源

例1:domain1.com 請求 domain2.com 上的 demo.woff2app

<!-- index.html -->
<style>
  @font-face {
    font-family: 'Demo';
    src: url(http://diary8.com/demo.woff2) format('woff2');
  }
</style>
<i style="font-family: Demo;">hi</i>

處理

# 請求1頭
Origin: https://domain1.com

# 響應1頭
Access-Control-Allow-Origin: https://domain1.com

3.讀取 因跨域圖片被污染的canvas

儘管不經過 CORS 就能夠在 <canvas> 中使用其餘來源的圖片,可是這會污染畫布,而且再也不認爲是安全的畫布,這將可能在 <canvas> 檢索數據過程當中引起異常

canvas 被污染 的狀況下不能使用此方法

  • ctx.getImageData(sx, sy, sw, sh)
  • canvas.toBlob(callback, type, encoderOptions)
  • canvas.toDataURL(type, encoderOptions)

例1:domain1.comnew Image() 讀取 domain2.com/demo.jpg 內容

const img = new Image();
img.crossOrigin = 'anonymous';
img.addEventListener('load', () => {
  // 1.繪製canvas,將圖片覆蓋在上面
  const domCanvas = document.createElement('canvas');
  const ctx = domCanvas.getContext('2d');
  domCanvas.width = img.width;
  domCanvas.height = img.height;
  ctx.drawImage(img, 0, 0);
  // 2.讀取canvas內容(此處受限)
  console.log(domCanvas.toDataURL());
});
img.src = 'https://domain2.com/demo.jpg';

處理

# 請求1頭
Origin: https://domain1.com

# 響應1頭
Access-Control-Allow-Origin: https://domain1.com

4.跨域圖片

<img src="https://domain2.com/demo.jpg" crossorigin="use-credentials">
const img = new Image();
img.crossOrigin = 'use-credentials';
img.addEventListener('load', () => {
  // 圖片加載完成
});
img.src = 'https://domain2.com/demo.jpg';

3.附:讀取image內容的2種方式

方法1: 經過canvas畫圖讀取

const img = new Image();
img.crossOrigin = 'anonymous';
img.addEventListener('load', () => {
  // 1.繪製canvas,將圖片覆蓋在上面
  const domCanvas = document.createElement('canvas');
  const ctx = domCanvas.getContext('2d');
  domCanvas.width = img.width;
  domCanvas.height = img.height;
  ctx.drawImage(img, 0, 0);
  // 2.讀取canvas內容(此處受限)
  console.log(domCanvas.toDataURL());
});
img.src = 'https://domain2.com/demo.jpg';

方法2:File讀取input file內容

<input id="img" type="file">
<script>
  const domImg = document.getElementById('img');
  domImg.addEventListener('change', () => {
    const reader = new FileReader();
    reader.addEventListener('load', () => {
      console.log(reader.result);
    });
    reader.readAsDataURL(domImg.files[0]);
  });
</script>
相關文章
相關標籤/搜索