跨域和CORS

248 阅读5分钟
  1. 什么是跨域?

    同源:协议、域名、端口相同。不同源即是跨域。两个不同的域名即使指向同一个IP地址,也不同源。

  2. 同源策略限制的内容有哪些?

    DOM 层面:限制了不同源 JavaScript 对当前 DOM 对象的读写操作;

    数据层面:限制了不同源站点读取当前站点的 Cookie、IndexDB、LocalStorage 等数据;

    网络层面:限制了通过 XMLHttpRequest 等方式将站点的数据发送给不同源的站点。

  3. 允许跨域的三个标签

    <img><link><script>

  4. 跨域的解决方案有哪些?

    (1) 同源策略限制下请求接口的方式:

    1. JSONP

      • 首先是利用<script>标签的src属性来实现跨域。
      • 通过将前端方法作为参数传递到服务器端,然后由服务器端注入参数之后返回,实现服务器端向客户端通信。
      • 由于使用<script>标签的src属性,因此只支持 get 方法。
    2. iframeform

      • 创建一个iframe元素和form元素;
      • 如果需要在响应返回时执行一些操作,可以在iframeload事件处理程序中添加相应代码;
      • 指定form元素的action属性为请求的urlmethod属性为posttarget属性为iframename,最后调用formsubmit()方法发送请求。
    3. CORS

      分为简单请求和应当先发送预检请求的请求。简单请求设置Origin头部,服务器返回响应数据时设置Access-Control-Allow-Origin头部。非简单请求先发送预检请求,预检请求完成后再发送实际请求。

    4. 代理

      使用nginx代理配置 server_name 为请求发起方的域名,然后设置location拦截前端的跨域请求,再将请求代理到服务器域名即可绕过浏览器的同源策略。

    5. WebSocket

    (2) 同源限制下Dom查询的方式

    1. postMessage

      使用 HTML5 提供的postMessage()方法可以实现不同页面的跨域通讯。在页面中使用postMessage()发送消息,使用addEventListener()监听回复的消息。

    2. document.domain

      适用于主域名相同,子域名不同的iframe跨域。给两个页面指定document.domain = 主域名即可访问对方的window对象了。

    3. canvas操作图片的跨域问题

      跨域的图片只要能在网页中正常显示,就可以使用canvasdrawImage()方法绘制出来。但是如果要对图片进行getImageData()toDataURL()操作,则需要处理跨域问题。

      • 响应头中设置Access-Control-Allow-Origin头部,允许请求发送方访问
      • 创建了Image实例后,设置实例的crossOrigin属性值为''crossOrigin的属性值不为use-credentials时,全部都会解析为anonymous,表示不需要携带任何非匿名信息给服务器)
  5. 什么是 CORS ?

    CORS 即跨域资源共享标准,它新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,对可能对服务器数据产生副作用的 HTTP 请求方法,浏览器必须先使用OPTIONS方法发起一个预检请求,从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证。

  6. CORS 的三个访问控制场景

    (1) 简单请求满足以下所有条件:

    1. 使用的请求方法为GETHEADPOST中的一种;

    2. 首部字段仅限于AcceptAccept-LanguageContent-LanguageContent-TypeDPRDownlinkSave-DataViewport-WidthWidth所组成的集合中的元素;

    3. Content-Type值为text/plainmultipart/form-dataapplication/x-www-form-urlencoded中的一个;

    4. 请求中的任意XMLHttpRequestUpload对象都没有注册任何事件监听器;

    5. 请求中没有使用ReadableStream对象。

      简单请求中头部字段Origin值为自己的域名,响应头部携带字段Access-Control-Allow-Origin,值为包含请求域名的值或*,这样即可完成跨域请求。

    (2) 应当先发送预检请求的请求满足下述任一条件:

    1. 使用的请求方法为PUTDELETECONNECTOPTIONSTRACEPATCH中的一种;
    2. 首部字段设置了AcceptAccept-LanguageContent-LanguageContent-TypeDPRDownlinkSave-DataViewport-WidthWidth之外的字段;
    3. Content-Type值不属于application/x-www-form-urlencodedmultipart/form-datatext-plain之一;
    4. 请求中的XMLHttpRequestUpload对象注册了任意多个事件监听器;
    5. 请求中使用了ReadableStream对象。

    发送一个使用OPTIONS方法的预检请求,用以从服务器获取更多信息。预检请求完成之后,发送实际请求。

    // 预检请求头部,这些头部无须手动设置,使用XMLHttpRequest对象发起跨域请求时,它们已经被设置就绪
    Origin: 请求来源
    Access-Control-Request-Method: 实际请求将使用的方法
    Access-Control-Request-Headers: 实际请求将使用的自定义首部字段
    
    // 预检请求响应头部
    Access-Control-Allow-Origin: 允许访问的域名
    Access-Control-Allow-Methods: 允许使用的请求方法
    Access-Control-Allow-Headers: 允许使用的自定义首部
    Access-Control-Max-Age: 响应的有效时间。在有效时间内,浏览器无须为同一请求再次发起预检请求
    

    (3) 附带身份凭证的请求

    • 跨域请求需要发送凭证信息时,XMLHttpRequest请求需要将withCredentials标识设置为trueFetch请求需要将credentials: 'include'添加到fetch()方法的init对象中。响应头部中需要携带Access-Control-Allow-Credentials: true头部,否则浏览器不会把响应内容返回给请求发送者。
    • 附带凭证的请求,服务器不可以设置Access-Control-Allow-Origin的值为*,如果设置了会导致请求失败。

参考:

  1. 【segmentfault】不要再问我跨域的问题了:segmentfault.com/a/119000001…
  2. 解决canvas图片getImageData, toDataURL跨域问题:www.zhangxinxu.com/wordpress/2…
  3. 【MDN】HTTP访问控制(CORS):developer.mozilla.org/zh-CN/docs/…