【web网络篇】—跨域

392 阅读9分钟

在这里插入图片描述

写在前面:

如何真正理解一个知识,我想只有从what why how这三个方面真正理解,才能进脑子。 因此我打算从三个方面,分别是:跨域是什么,为什么要跨域,跨域问题怎么解决来看有关跨域的知识点。

哪个不会写哪个,加油咯

先来看看全文总览: 在这里插入图片描述

在理解跨域之前,首先要理解的是同源策略

同源策略

同源:

如果两个 URL 的 protocol、port (如果有指定的话)和 host 都相同的话,则这两个 URL 是同源

同源策略:

同源策略控制不同源之间的交互,例如在使用XMLHttpRequest 或 标签时则会受到同源策略的约束。

跨域

再来说说跨域,总体上分为三个方面---网络,DOM,web数据;

  • 跨源网络访问
  • 跨源脚本API访问
  • 跨源数据存储访问

一、跨源网络访问

同源策略限制了通过XMLHttpRequest等方式将站点的数据发送给不同源的站点

同源策略是使用在两个不同源之间,用来限制它们之间交互的一种安全策略。 其中交互分为以下三种:

1、跨域写操作:

一般是被允许的,例如链接(links)重定向以及表单提交

2、跨域资源嵌入:

一般是被允许,有以下一些情况可以支持资源嵌入:

  • <script src="..."></script>标签嵌入跨域脚本。语法错误信息只能被同源脚本中捕捉到。

  • <link rel="stylesheet" href="..."> 标签嵌入CSS。由于CSS的松散的语法规则,CSS的跨域需要一个设置正确的 HTTP 头部 Content-Type 。不同浏览器有不同的限制: IE, Firefox, Chrome, Safari (跳至CVE-2010-0051)部分 和 Opera

  • 通过 <img> 展示的图片。支持的图片格式包括PNG,JPEG,GIF,BMP,SVG,...

3、跨域读操作:

一般是不被允许的,但常可以通过内嵌资源来巧妙的进行读取访问。例如,你可以读取嵌入图片的高度和宽度,调用内嵌脚本的方法

  • 如何允许跨源访问:

可以使用 CORS 来允许跨源访问。(下文具体讲)

CORS 是 HTTP 的一部分,它允许服务端来指定哪些主机可以从这个服务端加载资源。

  • 如何阻止跨源访问

阻止跨域写操作,只要检测请求中的一个不可推测的标记(CSRF token)即可,这个标记被称为Cross-Site Request Forgery (CSRF)标记。你必须使用这个标记来阻止页面的跨站读操作。

阻止资源的跨站读取,需要保证该资源是不可嵌入的。阻止嵌入行为是必须的,因为嵌入资源通常向其暴露信息。

阻止跨站嵌入,需要确保你的资源不能通过以上列出的可嵌入资源格式使用。浏览器可能不会遵守 Content-Type 头部定义的类型。例如,如果您在HTML文档中指定 <script> 标记,则浏览器将尝试将标签内部的 HTML 解析为JavaScript。 当您的资源不是您网站的入口点时,您还可以使用CSRF令牌来防止嵌入。

二、跨源脚本API访问

同源策略限制了来自不同源的Javascript脚本对当前DOM对象的读和写操作:

JavaScript 的 API 中,如 iframe.contentWindow、 window.parent、window.open 和 window.opener 允许文档间直接相互引用。当两个文档的源不同时,这些引用方式将对 Window 和 Location对象的访问添加限制

为了能让不同源中文档进行交流,可以使用 window.postMessage。(下文具体讲)

三、跨源数据存储访问

同源策略限制了来自不同源的站点读取当前站点的localStorage IndexedDBCookies

访问存储在浏览器中的数据,如 localStorage IndexedDB,是以源进行分割。每个源都拥有自己单独的存储空间,一个源中的 JavaScript 脚本不能对属于其它源的数据进行读写操作。而Cookies 使用不同的源定义方式。

最后来理一理有关如何解决跨域问题

解决跨域问题

1、JSONP

JSONP是一种应用Json的一种方法,是在被包含在函数调用中的JSON

callback({
  "name":"jingjing"
});

JSONP由两部分组成:回调函数和数据 一个简单的JSONP请求:

http://freegeoip.net/json/?callback=handleResponse

这个URL在请求一个JSONP地理定位服务。通过查询字符串来指定JSONP服务的回调函数是很常见的,这里的回调函数指的是handleResponse

回到JSONP是如何实现跨域的呢? JSNP是通过动态<script>元素来使用的,使用时可以为src属性指定一个跨域URL,<script>有能力不受限制地从其他域里加载资源。

2、CORS

跨源资源共享 (CORS) (或通俗地译为跨域资源共享)是一种机制,该机制使用附加的 HTTP 头来告诉浏览器,准许运行在一个源上的Web应用访问位于另一不同源选定的资源。

跨源HTTP请求的一个例子:

运行在 domain-a.com 的JavaScript代码使用XMLHttpRequest来发起一个到 domain-b.com/data.json 的请求。

CORS分为两种类型:

  • 简单请求
  • 附带身份凭证的请求

简单请求

某些请求不会触发 CORS 预检请求,至于CORS预检请求是什么,下面会提到。 以下请求方法满足“简单请求”:

  • 使用下列方法之一: GET,HEAD,POST
  • Content-Type 的值仅限于下列三者之一: text/plain

multipart/form-data

application/x-www-form-urlencoded

  • 请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。

  • 请求中没有使用 ReadableStream 对象。

有关预检请求: “需预检的请求”要求必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。

 if(invocation)
    {
      invocation.open('POST', url, true);
      invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
      invocation.setRequestHeader('Content-Type', 'application/xml');
     ....
    }

使用 POST 请求发送一个 XML 文档,该请求包含了一个自定义的请求首部字段(X-PINGOTHER: pingpong)。另外,该请求的 Content-Type 为 application/xml。因此,该请求需要首先发起“预检请求”。

预检请求报文:


OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

浏览器检测到,从 JavaScript 中发起的请求需要被预检。从上面的报文中,我们看到,发送了一个使用 OPTIONS 方法的“预检请求”。 OPTIONS 是 HTTP/1.1 协议中定义的方法,用以从服务器获取更多信息。该方法不会对服务器资源产生影响。

1.注意这个请求报文中的这个字段 Origin: http://foo.example 2.预检请求中同时携带了下面两个首部字段:

Access-Control-Request-Method: POST

Access-Control-Request-Headers: X-PINGOTHER, Content-Type

3.首部字段Access-Control-Request-Method告知服务器,实际请求将使用 POST 方法。

4.首部字段 Access-Control-Request-Headers 告知服务器,实际请求将携带两个自定义请求首部字段:X-PINGOTHER 与 Content-Type。

服务器据此决定,该实际请求是否被允许。

预检响应报文:


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

预检请求的响应,表明服务器将接受后续的实际请求,其中需要关注的是:

Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400

1.Access-Control-Allow-Origin: http://foo.example和上面请求报文中的Origin: http://foo.example一致

2.首部字段 Access-Control-Allow-Methods 表明服务器允许客户端使用 POST, GET 和 OPTIONS 方法发起请求

3.首部字段Access-Control-Allow-Headers表明服务器允许请求中携带字段 X-PINGOTHER 与 Content-Type Access-Control-Allow-Methods 一样,Access-Control-Allow-Headers 的值为逗号分割的列表

4.最后,首部字段 Access-Control-Max-Age 表明该响应的有效时间为 86400 秒,也就是 24 小时。在有效时间内,浏览器无须为同一请求再次发起预检请求。

请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效。

附带身份凭证的请求

对于跨源 XMLHttpRequest 或 Fetch 请求,浏览器不会发送身份凭证信息。如果要发送凭证信息,需要设置 XMLHttpRequest 的某个特殊标志位。

通过设置 withCredentials: true ,可以指定某个请求应该发生凭据,如果服务器接收凭据的请求,会用下面的HPPT头部来响应

Access-Control-Allow-Credentials: true 

如果服务器端的响应中未携带这个头部,浏览器将不会把响应内容返回给请求的发送者。

3、postMessage()

从广义上讲,一个窗口可以获得对另一个窗口的引用(比如 targetWindow = window.opener),然后在窗口上调用 targetWindow.postMessage() 方法分发一个 MessageEvent 消息。接收消息的窗口可以根据需要自由处理此事件。传递给 window.postMessage() 的参数(比如 message )将通过消息事件对象暴露给接收消息的窗口。

otherWindow.postMessage(message, targetOrigin, [transfer]);

otherWindow 其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。 message 将要发送到其他 window的数据。 targetOrigin 通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。

postMessage()方法接收两个参数:一条消息和一个表示消息接收方来自哪个域的字符串。第二个参数对保障安全通信非常重要,可以防止浏览器把消息发送到不安全的地方。来看下面的例子。

var iframeWindow = doucument.getElementById("jing").contentWindow;
iframeWindow.postMessage("da","http://www.jing.com");

其他跨域技术

正确面对跨域,别慌

参考文献:

[1] MDN文档

[2] 浏览器实践与原理

[3] Javascript高级程序设计

欢迎参观我的博客:婧婧的博客