什么是跨域?

要明白什么是跨域之前,首先要明白什么是同源策略

同源策略就是用来限制从一个源加载的文档或脚本与来自另一个源的资源进行交互。那怎样判断是否是同源呢?

如果协议,端口(如果指定了)和主机对于两个页面是相同的,则两个页面具有相同的源,也就是同源。也就是说,要同时满足以下3个条件,才能叫同源:

  1. 协议相同
  2. 端口相同
  3. 主机相同

举个例子就一目了然了:

我们来看下面的页面是否与 http://store.company.com/dir/index.html 是同源的?

  1. http://store.company.com/dir/index2.html 同源
  2. http://store.company.com/dir2/index3.html 同源 虽然在不同文件夹下
  3. https://store.company.com/secure.html 不同源 不同的协议(https)
  4. http://store.company.com:81/dir/index.html 不同源 不同的端口(81)
  5. http://news.company.com/dir/other.html 不同源 不同的主机(news)

所以当面对跨域问题的时候,有什么解决方案呢?

跨域的几种解决方案

document.domain方法

我们来看一个具体场景:有一个页面 http://www.example.com/a.html ,它里面有一个iframe,这个iframe的源是 http://example.com/b.html ,很显然它们是不同源的,所以我们无法在父页面中操控子页面的内容。

解决方案如下:

1
2
3
4
<!-- b.html -->
<script>
document.domain = 'example.com';
</script>
1
2
3
4
5
6
7
8
<!-- a.html -->
<script>
document.domain = 'example.com';
var iframe = document.getElementById('iframe').contentWindow.document;

//后面就可以操作iframe里的内容了...

</script>

所以我们只要将两个页面的document.domain设置成一致就可以了,要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域。

但是,这种方法只能解决主域相同的跨域问题。

window.name方法

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。

我们来看一个具体场景,在一个页面 example.com/a.html 中,我们想获取 data.com/data.html 中的数据,以下是解决方案:

1
2
3
4
<!-- data.html -->
<script>
window.name = 'data'; //这是就是我们需要通信的数据
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- a.html -->
<html>
<head>
<script>
function getData () {
var iframe = document.getElementById('iframe');
iframe.src = 'example.com/b.html'; // 这里让iframe与父页面同源

iframe.onload = function () {
var data = iframe.contentWindow.name; //在这里我们得到了跨域页面中传来的数据
};
}
</script>
</head>
<body>
</body>
</html>

JSONP方法

JONSP(JSON with Padding)是JSON的一种使用模式。基本原理如下:

1
2
3
4
5
6
7
8
<!-- a.html -->
<script>
function dealData (data) {
console.log(data);
}
</script>

<script src='http://example.com/data.php?callback=dealData'></script>
1
2
3
4
5
<?php
$callback = $_GET['callback'];
$data = 'data';
echo $callback.'('.json_encode($data).')';
?>

这时候在a.html中我们得到了一条js的执行语句dealData('data'),从而达到了跨域的目的。

所以JSONP的原理其实就是利用引入script不限制源的特点,把处理函数名作为参数传入,然后返回执行语句,仔细阅读以上代码就可以明白里面的意思了。

如果在jQuery中用JSONP的话就更加简单了:

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

注意jQuery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用JSONP的回调函数。

总结

除了上述方法外,HTML5还新增了一个window.postMessage()方法,有兴趣的可以自行查阅。