參考: HTTP訪問控制(CORS)
當頁面與頁面請求的資源 不在 同一域/協議/端口
時,會發起一個 跨域HTTP請求
。php
出於安全緣由,瀏覽器會限制從腳本內發起的跨源HTTP請求或響應。html
經過跨域資源共享 CORS(cross-origin share standard) 機制,可以使跨域數據傳輸安全進行。canvas
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 |
預檢請求:實際請求容許的自定義頭部 |
非預檢請求都是簡單請求。緩存
當請求知足下述任一條件時,將首先使用 OPTIONS
方法發起一個預檢請求到服務器,以獲知服務器是否容許該實際請求。安全
PUT
/ DELETE
/ CONNECT
/ OPTIONS
/ TRACE
/ PATCH
Accept
/ Accept-Language
/ Content-Language
/ Content-Type
/ DPR
/ Downlink
/ Save-Data
/ Viewport-Width
/ Width
application/x-www-form-urlencoded
/ multipart/form-data
/ text/plain
例1:domain1.com
上 request.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
例1:domain1.com
請求 domain2.com
上的 demo.woff2
app
<!-- 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
被污染
的canvas儘管不經過 CORS 就能夠在 <canvas>
中使用其餘來源的圖片,可是這會污染畫布,而且再也不認爲是安全的畫布,這將可能在 <canvas>
檢索數據過程當中引起異常
canvas 被污染
的狀況下不能使用此方法
例1:domain1.com
上 new 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
<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';
方法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>