跨域

本文主要涉及三种跨域方法:JSONP、CORS、postMessage。javascript

Q:为何会出现跨域问题?
A:出于浏览器的同源策略限制,浏览器会拒绝跨域请求。
*注:严格的说,浏览器并非拒绝全部的跨域请求,实际上拒绝的是跨域的读操做。浏览器的同源限制策略是这样执行的:php

  • 一般浏览器容许进行跨域写操做(Cross-origin writes),如连接,重定向;
  • 一般浏览器容许跨域资源嵌入(Cross-origin embedding),如 img、script 标签;
  • 一般浏览器不容许跨域读操做(Cross-origin reads)。*

Q:什么状况才算做跨域?
A:非同源请求,均为跨域。名词解释:同源 —— 若是两个页面拥有相同的协议(protocol),端口(port)和主机(host),那么这两个页面就属于同一个源(origin)。
html

img01
img01

Q:为何有跨域需求?
A:场景 —— 工程服务化后,不一样职责的服务分散在不一样的工程中,每每这些工程的域名是不一样的,但一个需求可能须要对应到多个服务,这时便须要调用不一样服务的接口,所以会出现跨域。html5

如何实现跨域

一般,最经常使用的跨域方式有如下三种:JSONP、CORS、postMessage。java

JSONP

单纯地为了实现跨域请求而创造的一个 trick。
【实现原理】
虽然由于同源策略的影响,不能经过XMLHttpRequest请求不一样域上的数据(Cross-origin reads)。可是,在页面上引入不一样域上的js脚本文件倒是能够的(Cross-origin embedding)。所以在js文件载入完毕以后,触发回调,能够将须要的data做为参数传入。
【实现方式(需先后端配合)】json

<script type="text/javascript">
    function dosomething(data){
        //处理得到的数据
    }
</script>
<script src="http://example.com/data.php?callback=dosomething"></script>复制代码
<?php
$callback = $_GET['callback'];//获得回调函数名
$data = array('a','b','c');//要返回的数据
echo $callback.'('.json_encode($data).')';//输出
?>复制代码

【JSONP的优缺点】
优势:兼容性好(兼容低版本IE)
缺点:1.JSONP只支持GET请求; 2.XMLHttpRequest相对于JSONP有着更好的错误处理机制后端

CORS

CORS 是W3C 推荐的一种新的官方方案,能使服务器支持 XMLHttpRequest 的跨域请求。CORS 实现起来很是方便,只须要增长一些 HTTP 头,让服务器能声明容许的访问来源。跨域

值得注意的是,一般使用CORS时,异步请求会被分为简单请求和非简单请求,非简单请求的区别是会先发一次预检请求。
【简单请求】
使用下列方法之一且没有人为设置对 CORS 安全的首部字段集合以外的其余首部字段:浏览器

  • GET
  • HEAD
  • POST
    - 仅当POST方法的Content-Type值等于下列之一才算做简单请求
           - text/plain
           - multipart/form-data
           - application/x-www-form-urlencoded复制代码

请求报文:安全

GET /resources/public-data/ 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 Referer: http://foo.example/examples/access-control/simpleXSInvocation.html Origin: http://foo.example复制代码

请求报文的第10行:Origin: foo.example 代表该请求来源于 foo.exmaple。
响应报文:

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61 
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
[XML Data]复制代码

响应报文的第4行:Access-Control-Allow-Origin: * 代表该资源能够被任意外域访问。

【非简单请求】

  1. 使用了下面任一 HTTP 方法:
  • PUT
  • DELETE
  • CONNECT
  • OPTIONS
  • TRACE
  • PATCH
  1. 人为设置了对 CORS 安全的首部字段集合以外的其余首部字段。该集合为:
  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type (but note the additional requirements below)
  • DPR
  • Downlink
  • Save-Data
  • Viewport-Width
  • Width
  1. Content-Type 的值不属于下列之一:
  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

发送真正请求前会先发送预检请求,如图所示:

img02
img02

1.第一条OPTIONS为预检请求,中同时携带了下面两个首部字段:

Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER复制代码

预检请求的Request中的Access-Control-Request-Method: POST,是告诉服务器,以后的实际请求将使用POST方式。
Access-Control-Request-Headers 是告诉服务器,实际请求将携带两个自定义请求首部字段:X-PINGOTHER 与 Content-Type。服务器据此决定,该实际请求是否被容许

预检请求的Response中的

Access-Control-Allow-Origin: foo.example // 标识可接受的跨域请求源;
Access-Control-Allow-Methods: POST, GET, OPTIONS //标识可接受的跨域请求方法,如GET、POST、OPTIONS;
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type //标识可接受的跨域请求自定义头;
Access-Control-Max-Age: 86400。 //标识本次预请求的有效时间(秒),期间内无需再发送预请求;

XMLHttpRequest 请求能够发送凭证请求(HTTP Cookies 和验证信息),一般不会跨域发送凭证信息,但也有一些状况须要打通不一样的登陆态,所以若是要发送凭证信息,须要设置 XMLHttpRequest 的某个特殊标志位。好比下面代码,能够把 XMLHttpRequest 的 withCredentials 设置为 true,这样浏览器就能跨域发送凭证信息。

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;复制代码

服务端返回的响应头中的 Access-Control-Allow-Credentials 字段存在且为 true 时,浏览器才会将响应结果传递给客户端程序。另外,Access-Control-Allow-Origin 必须指定请求源的域名,不然响应失败。

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.com
Access-Control-Allow-Credentials: true
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain复制代码

以下图所示为附带身份凭证的请求流程图:

img03
img03

postMessage

window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可使用它来向其它的window对象发送消息,不管这个window对象是属于同源或不一样源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。

otherWindow.postMessage(message, targetOrigin, [transfer]);复制代码
相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息