跨域资源(接口)共享

  1. 同源政策
  2. 同源与非同源
  3. Access-Control-Allow-Origin
  4. JSONP
  5. WebSocket
  6. 参考

同源政策

1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。

最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页”同源”。

同源与非同源

所谓”同源”指的是”三个相同”。

举例来说,http://www.example.com/dir/page.html这个网址,协议是http://``,域名是www.example.com,端口是80`(默认端口可以省略)。它的同源情况如下。

浏览器的同源政策”(same-origin policy),限制了在非同源情况下的三种行为:

本文主要讨论的是AJAX,也是就是接口的共享。

同源政策规定,AJAX请求只能发给同源的网址,否则就报错。

XMLHttpRequest cannot load http://www.example.dev/ajax/list.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://platform.example.dev' is therefore not allowed access.

除了架设服务器代理(浏览器请求同源服务器,再由后者请求外部服务),有几种方法规避这个限制。

Access-Control-Allow-Origin

Access-Control-Allow-Origin是HTML5中定义的一种服务器端返回Response header,用来解决资源(比如字体、ajax请求)的跨域权限问题。

它定义了该资源允许被哪个域引用,或者被所有域引用。google字体使用*表示字体资源允许被所有域引用,也就是说你为了让你的接口、资源在二级域名下面的所有子站使用,你就可以在响应的header中设置该值为*:

header("Access-Control-Allow-Origin:*");

但是,该域值却不能使用正则。这是比较坑的地方。所以我们需要另外一种思路,就是根据Request Headers的内容,决定一些需要的Response Headers的内容,这里定义规则如下:

根据Request的Origin: 进行决策,在Origin来自*.example.com(包括www.example.com; example.com;)时,Response Header中增加Access-Control-Allow-Origin:头的内容保持和Requset Headers中的Origin: 头URI中的protocol, domain, port内容,并一致。

JSONP

JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。

它的基本思想是,网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

首先,网页动态插入<script>元素,由它向跨源网址发出请求。

function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}

window.onload = function () {
addScriptTag('http://example.com/ip?callback=foo');
}

function foo(data) {
console.log('Your public IP address is: ' + data.ip);
};

上面代码通过动态添加<script>元素,向服务器example.com发出请求。注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字,这对于JSONP是必需的。
服务器收到这个请求以后,会将数据放在回调函数的参数位置返回。

foo({
"ip": "8.8.8.8"
});

由于<script>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。

WebSocket

WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。

下面是一个例子,浏览器发出的WebSocket请求的头信息(摘自维基百科)。

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

上面代码中,有一个字段是Origin,表示该请求的请求源(origin),即发自哪个域名。
正是因为有了Origin这个字段,所以WebSocket才没有实行同源政策。因为服务器可以根据这个字段,判断是否许可本次通信。如果该域名在白名单内,服务器就会做出如下回应。

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

参考

script>