1. 同源策略

同源策略是指协议相同、域名相同、端口相同。
同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。
设想这样一种情况:A网站是一家银行,用户登录以后,又去浏览其他网站。如果其他网站可以读取A网站的 Cookie,会发生什么?
很显然,如果 Cookie 包含隐私(比如存款总额),这些信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。
由此可见,”同源政策”是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。

2. CORS

浏览器将CORS分为两种请求:简单请求和非简单请求
对于简单请求,浏览器检测到ajax跨域,会在请求头上添加origin字段,origin字段表示当前请求来自哪个源。如果origin指定的源,在许可范围,服务端返回的响应头中会有:access-control-allow-origin、acess-control-allow-credentials、Access-Control-Expose-Headers。如果origin指定的源,不在许可范围,服务端会正常返回,但是响应头中不会有access-control-allow-origin,从而浏览器抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。
对于复杂请求,会先发一个OPTIONS类型的预检请求,会携带origin、Access-Control-Request-Method、Access-Control-Request-Headers请求头。服务器收到”预检”请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。返回的响应头中Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers
一旦服务器通过了”预检”请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

3. iframe+window.name解决跨域问题

window.name属性的神奇之处在于name 值在不同的页面(甚至不同域名)加载后依旧存在(如果没修改则值不会变化),并且可以支持非常长的 name 值(2MB)。
在A页面,创建一个iframe,iframe的src设置为跨域服务器的地址。然后在iframe onload的时候将iframe的src换成和A页面同一个域名下的proxy页面。然后因为iframe的域名和A是同域,因此就能拿到iframe.contentWindow.name。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<script type="text/javascript">
iframe = document.createElement('iframe');
iframe.style.display = 'none';
var state = 0;

iframe.onload = function() {
if(state === 1) {
var data = JSON.parse(iframe.contentWindow.name);
console.log(data);
iframe.contentWindow.document.write('');
iframe.contentWindow.close();
document.body.removeChild(iframe);
} else if(state === 0) {
state = 1;
iframe.contentWindow.location = 'http://localhost:81/cross-domain/proxy.html';
}
};

iframe.src = 'http://localhost:8080/data.php';
document.body.appendChild(iframe);
</script>
</body>

4. iframe+location.hash解决跨域问题

在A页面中创建一个iframe,iframe的src设置为跨域服务器的地址,跨域服务端改变iframe的location并将要传输的数据放在hash中传回。

1
2
3
4
5
6
7
8
9
10
11
12
<?php
// 如果有必要则进行数据处理 $_GET['..']
// code
// 返回的数据
$data = '{\"name\":\"hanzichi\",\"age\":10}';
echo
"
<script>
window.location = 'http://localhost:81/location-hash/proxy.html' + '#' + \"$data\";
</script>
"
?>

可以看到跨域服务器将iframe的location设置为http://localhost:81/location-hash/proxy.html,并回传数据。
然后iframe在onload中获取iframe.contentWindow.location.hash。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<body>
<script type="text/javascript">
function getData(url, fn) {
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = url;

iframe.onload = function() {
fn(iframe.contentWindow.location.hash.substring(1));
window.location.hash = '';
document.body.removeChild(iframe);
};
document.body.appendChild(iframe);
}

// get data from server
var url = 'http://localhost:8080/data.php';
getData(url, function(data) {
var jsondata = JSON.parse(data);
console.log(jsondata.name + ' ' + jsondata.age);
});
</script>
</body>