跨域资源共享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>
相关文章
相关标签/搜索