浏览器跨域策略

在前端开发中跨域取资源是非常常见的,因为浏览器的保护策略,导致不能直接跨域,必须通过一些方法来实现..解决跨域问题的方法非常多,要根据具体需求来选择.

浏览器跨域策略

同一个域名下的 允许通信:http://www.baidu.com/a.js http://www.baidu.com/b.js

同一个域名下的不同文件夹 允许通信:http://www.baidu.com/a.js http://www.baidu.com/src/b.js

同一个域名的不同端口 不允许通信:http://www.baidu.com/a.js http://www.baidu.com:8080/b.js

域名对应IP的 不允许通信:http://www.baidu.com/a.js http://192.168.1.1/a.js

主域名相同 子域不同 不允许通信:http://www.baidu.com/a.js http://script.baidu.com/a.js

不同域名 不允许通信:http://www.baidu.com/a.js http://www.google.com.hk/b.js

如果是因为协议和端口导致的跨域问题,前端是无能为力的
在跨域问题上,域只会通过URL首部来判断,而不会判断两个域是否对应相同的ip上

跨域的几种办法

跨域的解决办法有分为前端办法与后端办法

通过document.domain来跨域

浏览器的同源策略其中之一是不能通过ajax的方法去请求不同源中的文档
另外一个限制是限制浏览器不同域的框架之间的交互
例如有一个页面,地址是http://www.baidu.com/a.html里面的页面有一个iframe,指向http;//baidu.com/b.html,显然两个框架是属于不同域的,所以我们无法通过在页面中书写js代码来获取iframe中的内容

a.html url:http://www.baidu.com/a.html

b.html url:http://baidu.com/b.html

1
2
3
4
5
6
7
8
9
<script type="text/javascript">
function test(){
var iframe = document.getElementById('iframe');
var win = iframe.contentWindows;//这里可以去得到另外一个不同域的iframe的window对象,但是这个对象的属性和方法基本上是取不到的
var doc = win.document;//error
var name = win.name;//error
}
</script>
<iframe id = "iframe" src = "http://baidu.com/b.html" onload = "test()"></iframe>

此时,如果我们要突破这个限制,可以将其中一个iframe的domain设置为相同的域,注意document.domain的设置是有限制的,我们只能将document.domain设置为自身或更高一级的父域,并且主域是要相同的

  • 在页面http://www.baidu.com/a.html中设置document.domain为baidu.com,也就是主域

    1
    2
    3
    4
    5
    6
    7
    <iframe id = "iframe" src="http://baidu.com/b.html" onload = "test()"></iframe>
    <script type="text/javascript">
    document.domain = 'baidu.com';//设置成主域
    function test(){
    alert(document.getElementById('iframe').contentWindow);//contentWindow 可取得子窗口的 window 对象
    }
    </script>
  • 在页面http://baidu.com/b.html中也设置document.domain为主域

    1
    2
    3
    <script type="text/javascript">
    document.domain = 'baidu.com';//在iframe载入这个页面也设置document.domain,使之与主页面的document.domain相同
    </script>

通过location.hash跨域 –锚点技术

原理:通过改变URL的hash部分来进行双向通信,每个window通过修改其他window的location来发送消息.

需要注意的:IE和Chrome浏览器不允许修改parent.location.hash的值,需要借助父窗口域名下的一个代理iframe

例如,父页面baidu.com/a.html iframe嵌入的页面是google.com/b.html
要实现两个页面的双向通信可以通过:

  • a.html传递数据到b.html

    • a.html修改iframe的src为google.com/b.html#paco
    • b.html监听到url的变化,触发对应的操作
  • b.html传递数据到a.html

    • 首先先在b.html下创建一个隐藏的iframe,此iframe时baidu.com域下的,并且挂上需要传递的hash数据
    • proxy.html监听到url的变化,修改a.html的url值,因为a.html与proxy.html是同域的,所以可以修改
    • a.html监听到url的变化,触发对应的操作

b页面的关键代码

1
2
3
4
5
6
7
8
9
try {
parent.location.hash = 'data';
} catch (e) {
// ie、chrome的安全机制无法修改parent.location.hash,
var ifrproxy = document.createElement('iframe');
ifrproxy.style.display = 'none';
ifrproxy.src = "http://www.baidu.com/proxy.html#data";
document.body.appendChild(ifrproxy);
}

proxy.html的关键代码

1
2
//因为parent.parent(即baidu.com/a.html)和baidu.com/proxy.html属于同一个域,所以可以改变其location.hash的值
parent.parent.location.hash = self.location.hash.substring(1);

使用有src的标签

原理:所有具有src的属性的标签都是可以跨域的,包括img``script

限制: 需要创建一个DOM对象,只能用于GET方法

不同的标签发送http请求的时机是不一样的

img标签在更改src属性的时候就会发送请求,但是scriptiframe只有在添加到DOM树之后才会发送http请求

1
2
3
4
5
var img = new Image();
img.src = 'http://some/picture'; // 发送HTTP请求
var ifr = $('<iframe>', {src: 'http://b.a.com/bar'});
$('body').append(ifr); // 发送HTTP请求

JSONP

原理:<script>是可以跨域的,并且跨域的脚本中可以直接回调我们定义的url参数中制定的函数

限制:只能用于GET方法

例如,有一个a.html页面,里面的代码需用利用ajax来获取不同域下的json数据,假设这个json数据的地址是http://example.com/data.php,那么在a页面下的代码可以是:

1
2
3
4
5
6
7
<script>
function dosomething(jsondata){
//处理json
}
</script>
<!--注意callback-->
<script src="http://example.com/data.php?callback=dosomething">

对应的后台接受到这个请求后,进行处理,返回数据,后台放回的一定是一个可以执行的js文件

1
2
3
4
5
<?php
$callback = $_GET['callback'];//取得回调的函数名
$data = array('a','b','c');//要返回的数据
echo $callback.'('.json_encode($data).')';
?>

最后页面输出的是
dosomething(['a','b','c'])

JQuery中有帮我们封装了一个JSONP的方法

可以免去我们手动插入script标签和定义回调函数,JQuery会自动生成一个全局函数来替换callback后的问号,之后获取到数据后就会销毁,这个方法会自动判断是否跨域,如果不跨域的话,就改用普通的ajax方法,如果跨域,就调用jsonp的回调函数

1
2
3
4
5
<script>
$.getJSON('http://example.com/data.php?callback=?',function(jsondata){
//处理json
});
</script>

原理:iframe之间共享navigation对象,可以用他来传递数据

限制:只用用在IE6/7上

不做过多介绍

CORS 跨域资源共享

原理:服务端通过设置Access-Control-Allow-Origin的HTTP响应头后,浏览器会允许跨域

限制:浏览器需要支持HTML5

因此,实现CORS通信的关键在于服务器,只要服务器实现的CORS的接口,就可以跨源通信

例如http://a.com要访问http://b.com的数据,这个时候通常会报错,只需要在被访问的http://b.com的服务器中设置Access-Control-Allow-Origin即可
允许跨域访问

1
2
Access-Control-Allow-Origin:* //*代表所有的域名
Access-Control-Allow-Origin: http://a.com #允许这个a.com域名下的访问

使用window.postMessage

原理: HTML5允许窗口之间发送消息

限制:需要HTML5的支持,并且获取窗口句柄后才能互相通信

1
2
3
//URL:http://a.com/foo
var win = window.open('http://b.com/bar')
win.postMessage('Hello world','http://b.com');
1
2
3
4
//URL://http://b.com/bar
window.addEventListener('message',function(event){
console.log(event.data)
});

总结

在HTML5之前,JSONP是一个跨域的事实标准,jQuery都给予支持,但是HTML5出现后,最好还是用CORS头和window.postMessage来,从机制上解决跨域的问题。值得注意的是Access-Control-Allow-Origin头字段是资源所在的服务器上设置的,访问控制的责任仍然是资源提供方,这是和JSONP一致的

相关阅读

js的跨域操作window.name和jsonp

文章目录
  1. 1. 浏览器跨域策略
    1. 1.1. 跨域的几种办法
      1. 1.1.1. 通过document.domain来跨域
      2. 1.1.2. 通过location.hash跨域 –锚点技术
      3. 1.1.3. 使用有src的标签
      4. 1.1.4. JSONP
        1. 1.1.4.1. JQuery中有帮我们封装了一个JSONP的方法
      5. 1.1.5. navigation对象
      6. 1.1.6. CORS 跨域资源共享
      7. 1.1.7. 使用window.postMessage
      8. 1.1.8. 总结
      9. 1.1.9. 相关阅读