跨域

为什么会有跨域?因为浏览器的同源策略。

同源策略

源相同。

如何区别是否是同一个源?

  • 协议相同
    js 中通过 location.protocol 获取协议。
    http、https是不同协议。
  • 端口相同
    js 中通过 location.port 获取端口。
    HTTP 端口默认是 80 。
    HTTPS 端口默认是 443 。
  • 域名相同,包括主域、子域名。
    js 中通过 location.hostname 获取域名。
    http://fanyi.youdao.com。fanyi 是子域,youdao.com 是主域。www.youdao.com 与 youdao.com 也是属于不同的域名。

同源策略限制了从同一个源加载的文档(document,主要是 dom 相关)或脚本(js,主要指 XMLHttpRequest )如何与来自另一个源的资源进行交互。

Cookie中的同源只关注域名,忽略协议和端口。

个人理解主要就是两方面:

  1. 前端与前端的同源限制
    页面中引入 iframe ,iframe 的源是否与当前页的源需要相同。
  2. 前端与后端的同源限制
    前端的页面的源需要与后端提供的接口的源相同。

如果没有同源策略,会有什么危害?

  • 获取文档中的 dom 信息
    可以利用 iframe 嵌套一些正规网站,除了网址不一样,其他都一样(效果有点类似钓鱼网站,不过钓鱼不是用 iframe ,是做的样式一样)。可以获取用户信息(密码、支付信息等)。
  • CSRF(跨站请求伪造)
    获取其他正规网站的 cookie ,然后用 cookie 模拟用户操作,如支付、转账。

所以同源策略是一个用于隔离潜在恶意文件的重要安全机制。

解决跨域通信

  • 前端与前端的跨域
    不同主域的前端页面之间,不能实现互相通信。所以主要解决的是主域相同的,子域不同的互相通信问题。
    • document.domain
      当前页和 iframe 页面都设置相同的 document.domain,则当前页可以拿到 iframe 中的 dom 节点。
      document.domain 只能设置成自身或更高一级的父域,且主域必须相同。
      这种方式非常适用于 iframe 跨域的情况。
    • postMessage
      window.postMessage(message,targetOrigin) 方法是 HTML5 新引进的特性,可以使用它来向其它的 window 对象发送消息,无论这个 window 对象是属于同源或不同源。
      调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 * 。
      需要接收消息的window对象,可是通过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中。
      可以实现双向通信,但是当前页获取不到 iframe 中的 dom 节点。
      当前页:
      iframe:
  • 前端与后端的跨域
    • 代理
      同源的后端去请求获取不同源的数据,再返给同源的前端。
    • JSONP
      主要原理是利用了script 标签可以跨域请求的特性,由其 src 属性发送请求到服务器,服务器返回 JavaScript 代码,浏览器接受响应,然后就直接执行了,这和通过 script 标签引用外部文件的原理是一样的。
      只有 get 方法( script 加载资源就是 get ),前端本地定义方法,返回值为执行该方法,本地定义的方法名称需要和返回值执行的方法名一样。
      后端代码: 前端代码:
    • CORS
      跨源资源共享机制

CORS

跨源资源共享机制(Cross-Origin Resource Sharing)允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。浏览器支持在 API 容器中(如 XMLHttpRequest 或 Fetch )使用 CORS,以降低跨域 HTTP 请求所带来的风险。

CORS 需要客户端和服务器同时支持。

跨域资源共享标准

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

跨域资源共享标准允许在下列场景中使用跨域 HTTP 请求:

  • XMLHttpRequest 或 Fetch 发起的跨域 HTTP 请求。

  • Web 字体 (CSS 中通过 @font-face 使用跨域字体资源)

  • WebGL 贴图

  • 使用 drawImage 将 Images/video 画面绘制到 canvas

  • 样式表

  • scripts

访问控制场景

使用三个场景来解释跨域资源共享机制的工作原理。这些例子都使用 XMLHttpRequest 对象。

简单请求

某些请求不会触发 CORS 预检请求。本文称这样的请求为“简单请求”,请注意,该术语并不属于 Fetch (其中定义了 CORS)规范。

若请求满足所有下述条件,则该请求可视为“简单请求”:

  • 使用下列方法之一

    • GET

    • HEAD

    • POST

  • 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 对象。

预检请求

“预检请求”要求必须首先使用 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 的值不属于下列之一

    • text/plain

    • multipart/form-data

    • application/x-www-form-urlencoded

  • 请求中的 XMLHttpRequestUpload 对象注册了任意多个事件监听器。

  • 请求中使用了 ReadableStream 对象。

附带身份凭证的请求

一般而言,对于跨域 XMLHttpRequest 或 Fetch 请求,浏览器不会发送身份凭证信息。如果要发送凭证信息,需要设置 XMLHttpRequest 的 withCredentials 为 true 。

如果服务器端的响应中未携带 Access-Control-Allow-Credentials: true ,浏览器将不会把响应内容返回给请求的发送者。

对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为“*”。

posted @ 2019-06-20 16:32  focus_yaya  阅读(216)  评论(0编辑  收藏  举报