跨域方法以及怎么样实现的与原理

400 阅读7分钟
同源安全策略

通过 XHR 实现 Ajax 通信的一个主要限制,来源于跨域安全策略。默认情况下,XHR 对象只能访问与包含它的页面位于同一个域中的资源。这种安全策略可以预防某些恶意行为。但是,实现合理的跨域请求对开发某些浏览器应用程序也是至关重要的。

CORS(Cross-Origin Resource Sharing,跨源资源共享)

CORS(Cross-Origin Resource Sharing,跨源资源共享)是 W3C 的一个工作草案,定义了在必须访 问跨源资源时,浏览器与服务器应该如何沟通。CORS 背后的基本思想,就是使用自定义的 HTTP 头部 让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。 比如一个简单的使用 GET 或 POST 发送的请求,它没有自定义的头部,而主体内容是 text/plain。在 发送该请求时,需要给它附加一个额外的 Origin 头部,其中包含请求页面的源信息(协议、域名和端 口),以便服务器根据这个头部信息来决定是否给予响应。下面是 Origin 头部的一个示例: Origin: www.a.com 如果服务器认为这个请求可以接受,就在 Access-Control-Allow-Origin 头部中回发相同的源 信息(如果是公共资源,可以回发"*")。例如: Access-Control-Allow-Origin: www.a.com 如果没有这个头部,或者有这个头部但源信息不匹配,浏览器就会驳回请求。正常情况下,浏览器 会处理请求。注意,请求和响应都不包含 cookie 信息。

图像Ping

跨域请求技术是使用标签。动态创建图像经常用于图像 Ping。图像 Ping 是与服务器进行简单、单向的跨域通信的一种方式。 请求的数据是通过查询字符串形式发送的,而响应可以是任意内容,但通常是像素图或 204 响应。通过 图像 Ping,浏览器得不到任何具体的数据,但通过侦听 load 和 error 事件,它能知道响应是什么时 候接收到的。

var img = new Image();
img.onload = img.onerror = function(){
    alert("Done!");
};
img.src = "http://www.example.com/test?name=Nicholas";

这里创建了一个 Image 的实例,然后将 onload 和 onerror 事件处理程序指定为同一个函数。这 样无论是什么响应,只要请求完成,就能得到通知。请求从设置 src 属性那一刻开始,而这个例子在请 求中发送了一个 name 参数。 图像 Ping 最常用于跟踪用户点击页面或动态广告曝光次数。图像 Ping 有两个主要的缺点,一是只 能发送 GET 请求,二是无法访问服务器的响应文本。因此,图像 Ping 只能用于浏览器与服务器间的单 向通信。

JSONP

JSONP 是 JSON with padding(填充式 JSON 或参数式 JSON)的简写,是应用 JSON 的一种新方法, 在后来的 Web 服务中非常流行。 JSONP 由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调 函数的名字一般是在请求中指定的。而数据就是传入回调函数中的 JSON 数据。下面是一个典型的 JSONP请求。 http://freegeoip.net/json/?callback=handleResponse

function handleResponse(response){
alert("You’re at IP address " + response.ip + ", which is in " +
          response.city + ", " + response.region_name);
}
var script = document.createElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse"; document.body.insertBefore(script, document.body.firstChild);
优点:
  • 非常简单易用
  • 能够直接访问响应文本,支持在浏览器与服务器之间双向通信
缺点
  • JSONP 是从其他域中加载代码执行。如果其他域不安全,很可能会在响应中夹带一些恶意代 12 码,而此时除了完全放弃 JSONP 调用之外,没有办法追究。因此在使用不是你自己运维的 Web 服务时, 一定得保证它安全可靠。
  • 不容易确定 JSONP 请求是否失败,开发人员不得不使用计时器检测指定时间内是否接收到了响应。但就算这样也不能尽如人意,毕竟不是每个用户上网的速度和带宽都一样。
Comet

Comet 能够让信息近乎实时地被推送到页面上,非常适合处理体育比赛的分数和股票报价。 有两种实现 Comet 的方式:长轮询和流。长轮询是传统轮询(也称为短轮询)的一个翻版,即浏览 器定时向服务器发送请求,看有没有更新的数据。 长轮询把短轮询颠倒了一下。页面发起一个到服务器的请求,然后服务器一直保持连接打开,直到 有数据可发送。发送完数据之后,浏览器关闭连接,随即又发起一个到服务器的新请求。这一过程在页 面打开期间一直持续不断。 轮询的优势是所有浏览器都支持,因为使用 XHR 对象和 setTimeout()就能实现。而你要做的就 是决定什么时候发送请求。 第二种流行的 Comet 实现是 HTTP 流。流不同于上述两种轮询,因为它在页面的整个生命周期内只 使用一个 HTTP 连接。具体来说,就是浏览器向服务器发送一个请求,而服务器保持连接打开,然后周 期性地向浏览器发送数据。 通过侦听 readystatechange 事件及检测 readyState 的值是否为 3,就可以利用 XHR 对象实现 HTTP 流。

服务器发送事件

SSE(Server-Sent Events,服务器发送事件)是围绕只读 Comet 交互推出的 API 或者模式。SSE API 用于创建到服务器的单向连接,服务器通过这个连接可以发送任意数量的数据。服务器响应的 MIME 类型必须是 text/event-stream,而且是浏览器中的 JavaScript API 能解析格式输出。SSE 支持短轮 询、长轮询和 HTTP 流,而且能在断开连接时自动确定何时重新连接。有了这么简单实用的 API,再实 现 Comet 就容易多了。 支持 SSE 的浏览器有 Firefox 6+、Safari 5+、Opera 11+、Chrome 和 iOS 4+版 Safari。

  1. SSE API SSE 的 JavaScript API 与其他传递消息的 JavaScript API 很相似。要预订新的事件流,首先要创建一 个新的 EventSource 对象,并传进一个入口点: var source = new EventSource("myevents.php"); 注意,传入的 URL 必须与创建对象的页面同源(相同的 URL 模式、域及端口)。EventSource 的 实例有一个 readyState 属性,值为 0 表示正连接到服务器,值为 1 表示打开了连接,值为 2 表示关闭 了连接。 另外,还有以下三个事件。  open:在建立连接时触发。  message:在从服务器接收到新事件时触发。  error:在无法建立连接时触发。 就一般的用法而言,onmessage 事件处理程序也没有什么特别的。 source.onmessage = function(event){ var data = event.data; //处理数据 }; 服务器发回的数据以字符串形式保存在 event.data 中。 默认情况下,EventSource 对象会保持与服务器的活动连接。如果连接断开,还会重新连接。这 就意味着 SSE 适合长轮询和 HTTP 流。如果想强制立即断开连接并且不再重新连接,可以调用 close() 方法。 source.close();

  2. 事件流 所谓的服务器事件会通过一个持久的 HTTP 响应发送,这个响应的 MIME 类型为 text/event-stream。响应的格式是纯文本,最简单的情况是每个数据项都带有前缀 data:,例如: data: foo data: bar data: foo data: bar 对以上响应而言,事件流中的第一个 message 事件返回的 event.data 值为"foo",第二个 message 事件返回的 event.data 值为"bar",第三个 message 事件返回的 event.data 值为 "foo\nbar"(注意中间的换行符)。对于多个连续的以 data:开头的数据行,将作为多段数据解析, 每个值之间以一个换行符分隔。只有在包含 data:的数据行后面有空行时,才会触发 message 事件, 因此在服务器上生成事件流时不能忘了多添加这一行。 通过 id:前缀可以给特定的事件指定一个关联的 ID,这个 ID 行位于 data:行前面或后面皆可: data: foo,id: 1 设置了 ID 后,EventSource 对象会跟踪上一次触发的事件。如果连接断开,会向服务器发送一个 包含名为 Last-Event-ID 的特殊 HTTP 头部的请求,以便服务器知道下一次该触发哪个事件。在多次 连接的事件流中,这种机制可以确保浏览器以正确的顺序收到连接的数据段。