Ajax跨域的解决方法

之前在项目中用 HTML5 提供的 XHR2 草草地临时性解决了一个 ajax 的跨域问题。其实跨域这东东还是大有乾坤的,今天来深入扒一扒。

1 什么是跨域

同源策略可以一定程度上保证web的安全。跨域,简单地说:由于同源策略的限制,a.com 域名下无法跟 b.com 域名进行数据的请求交互。

比如,我在一个合法的网站A上注册了个人信息;注册完了之后,我又去一家不安全的网站B去转转。万一恶意网站B、跑去合法网站A、盗取了我的个人信息……后果是不是很严重的样子?所以同源策略还是为咱用户着想的。但是有时候,在安全的情况下,我们又希望可以突破同源策略限制去跨域请求。这又该怎么办呢?

具体属于跨域的情况如下:

域名url 说明 跨域情况
http://www.a.com/a.js
http://www.a.com/b.js
同一域名下不同文件 允许
http://www.a.com/lib/a.js
http://www.a.com/main/b.js
同一域名下不同文件路径的文件 允许
http://www.a.com:4000/a.js
http://www.a.com/b.js
同一域名、端口不同 不允许
http://www.a.com/a.js
https://www.a.com/b.js
同一域名、协议不同 不允许
http://www.a.com/a.js
http://127.0.0.1/b.js
域名、域名对应的ip 不允许
http://www.a.com/a.js
http://ccc.a.com/b.js
主域名(一级)同、子域名(二级)不同 不允许
http://www.a.com/a.js
http://www.b.com/b.js
域名完全不同 不允许

何为同源?总结:协议相同、端口相同、域名相同

同源策略主要对三种跨域行为造成了限制:

  1. ajax 请求报错
  2. cookie、localStorage、indexDB 无法读取
  3. DOM 新窗口(iframe,window.open)获取不到

2 ajax 跨域的解决方法

2.1 jsonp 解决 ajax 跨域

jsonp 原理:
同源策略下,在某个服务器下的页面是无法获取到该服务器以外的数据的,但img、iframe、script等标签是个例外,这些标签可以通过src属性请求到其他服务器上的数据。

利用script标签的开放策略,我们可以实现跨域请求数据,当然,也需要服务端的配合。当我们正常地请求一个JSON数据的时候,服务端返回的是一串JSON类型的数据,而我们使用JSONP模式来请求数据的时候,服务端返回的是一段可执行的JavaScript代码。

jsonp 实现的过程:
后台页面 ‘http://www.bbb.com/user‘ 返回的 jsonp 数据如下:

1
foo({'id':123,'name':'guo''jop':'web'});

请求页面 http://www.aaa.com/ 中: 动态创建<script> 标签跨域调用、并定义函数 foo

1
2
3
4
5
6
<script src="http://www.aaa.com/user?id=123?callback=foo"></script>
function foo(data){
console.log('id为123的name值是:'+data.name);
}

jquery中使用jsonp:

1
2
3
4
5
6
7
8
9
10
11
12
13
//注意:callback=?
$.getJSON('http://www.aaa.com/user?id=123?callback=?',function(data){
console.log('id为123的name值是:'+data.name);
});
//注意:dataType:'jsonp'
$.ajax({
type:'get',
dataType:'jsonp',
url:'http://www.aaa.com/user?id=123',
success:function(data){
console.log('id为123的name值是:'+data.name);
}
});

JavaScript中使用jsonp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function foo(data){
console.log('id为123的name值是:'+data.name);
}
function loadScript(srcUrl){
var script=document.createElement('script');
script.type='text/javascript';
script.src=srcUrl;
document.body.appendChild(script);
}
window.onload=function(){
loadScript("http://www.aaa.com/user?id=123?callback=foo");
}

2.2 代理(中间过渡)解决 ajax 跨域

代理解决 ajax 跨域问题的实现技术主要偏后台,这里只是简单地介绍一下实现的原理。

在域 domain1.com 下有2个文件:a.phpdaili.php
在域 domain2.com 下有1个文件:b.php
domain1.coma.phpdomain2.comb.php 请求,跨域不允许;

domain1.coma.phpdomain1.comdaili.php 请求,允许;
在服务器端,domain1.comdaili.phpdomain2.comb.php 请求,允许; daili.php 吧请求到的数据再返回给同域的 a.php ,完成。

2.3 CORS跨域资源共享

( 参考:javascipt高级程序设计 )
CORS,跨域资源共享。它定义了在必须访问跨域资源时、浏览器与服务器应该如何沟通。CORS的基本思想,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求应该成功还是失败。

比如,一个简单的 get 或 post 请求,它没有自定义的头部;需要给它附加一个额外的 origin 头部,包含请求页面的源信息(协议、端口、域名);服务器根据这个头部信息来决定是否给予响应。

IE和其它浏览器对 CORS 的实现方式各不相同(IE8支持)。这里写一个跨浏览器的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function createCORSRequest(method, url){
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr){
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined"){
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
xhr = null;
}
return xhr;
}
var request= createCORSRequest("get", "http://www.xxx.com");
if (request){
datas.onload = function(){
//对request.responseText服务器中返回的数据进行处理
};
request.send();
}

HTML5 提供的XHR2
HTML5 提供的 XMLHttpRequest Level2 已经实现了跨域访问,只是 IE10支持。只需要在后台PHP中设置表头 header 即可。

比如:http://www.aaa.com 打算跨域向 http://www.bbb.com 请求数据,报错“源不匹配”;这时,我们在 http://www.bbb.com 设置 header 即可。

1
2
3
header(‘Access-Control-Allow-Origin:http://www.aaa.com’);
//header(‘Access-Control-Allow-Origin:*’);
header(‘Access-Control-Allow-Origin-Methods:POST,GET’);

通过设置 document.domain 共享 cookie。
如:一级域名相同、二级域名不同的两个页面:
http://www.aaa.com/a.html
http://yyy.aaa.com/b.html
以上2个页面均设置相同的 domaindocument.domain = 'aaa.com';
这样,两个页面就可以互相读取对方所设置的 cookie 了。

注意:cookie 的路径。

4 iframe 跨域的解决方法(略)

感觉 iframe 这东西暂时用得比较少,此处略为学习一下。

iframe 和 window.open 方法打开的不是同源的窗口,它们与父窗口无法通信。

解决方法大概主要有以下四种:

  1. window.name
  2. window.postMessage (html5)
  3. localStorage
  4. URL的#号hash值

学习参考: