CORS(Cross-Origin Resource Sharing)
服务端设置跨域资源共享,比如nodejs
//设置允许的域res.header('Access-Control-Allow-Origin', req.headers.origin); //其值为*或具体域
//设置允许的Header(如果有自定义的头则会发送预请求)res.header('Access-Control-Allow-Headers', 'X-Requested-With'); //x-requested-with XMLHttpRequest
1 | //服务端设置 |
CORS依据请求方式不同可以分为简单验证请求和预请求
简单请求
是指:1.只使用 GET, HEAD 或者 POST 请求方法。如果使用 POST 向服务器端传送数据,则数据类型(Content-Type)只能是 application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一种。
2.不会使用自定义请求头(类似于 X-Modified 这种)。
预请求
不同于上面讨论的简单请求,“预请求”要求必须先发送一个 OPTIONS。
例:
agent_call_list?agentId=747316 OPTIONS 200 app.sh.fangdd.test 10.0.1.81:80 545 B 6 ms nginx/1.4.7
agent_call_list?agentId=747316 GET 200 app.sh.fangdd.test 10.0.1.81:80 578 B 29 ms nginx/1.4.7
附带凭证的请求
即会带上包含cookie的session, 必须为服务端设置的cookie或session才会自动带上。
需要将XMLHttpRequest的withCredentials标志设置为true。withCredentials = true
JSONP
原理是:<script>
可以跨域加载远程js文件,由服务端返回包含数据的由前端指定回调名称的js方法体。
可以利用jquery的jsonp或手动创建<script>
去实现。
具体实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17function jsonp(url, callback){
window.myJsonpCallback = function(data){
callback(data);
delete window.myJsonpCallback;
};
//创建script标签引入外部带data的script url
var script = document.createElement('script');
script.src = url+'?callback=myJsonpCallback';
document.body.appendChild(script);
script.onload = script.onreadystatechange = function() {
if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
//解析带data的script url;
script.onload = script.onreadystatechange = null;
}
};
}
jsonp的返回体一般为形如:1
2
3
4myJsonpCallback({
'name':1,
'email':2
});
利用iframe
iframe + window.name实现的跨域数据传输
原理核心:window对象的name属性是一个很特别的属性,当该window的location变化,然后重新加载,它的name属性可以依然保持不变。
依此原理,我们可以在页面A中用iframe加载其他域的页面B,而页面B中用JavaScript把需要传递的数据赋值给 window.name,页面A的iframe加载完成之后,页面A修改iframe的地址,将其变成同域的一个地址,然后就可以读出window.name的值了。
简单来说:利用a页面iframe加载远程页面b的window.name属性,原后将iframe的值指向同源页面localproxy.html,读取同源页面中没有变化的 window.name
。
如下代码:
本地页面local.html创建一个iframe,其值指向远程页面url=>data.html,获取远程url上面的window.name,所以data.html应该是这样:指定window.name的值为数据
(window.name的值只能为字符串,为object时不能解析出里面的结构):1
2
3
4
5
6
7
8
9
10
11
12
13
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
window.name = 'data';
</script>
</body>
</html>
1 | /* url:远程url, |
iframe + document.domain
原理是:将不同子域的的域名设置为主域:document.domain = ‘main.com’; 原来某域文件中创建iframe,去控制iframe的contentDocument。所以仅适用于主域相同而二级域名不同的情况。1
2
3
4
5
6
7
8
9
10
11
12function getSubDomainData(host, url, callback){
document.domain = host;
var ifr = document.createElement('iframe');
ifr.src = url;
ifr.style.display = 'none';
document.body.appendChild(ifr);
ifr.onload = function(){
var doc = ifr.contentDocument || ifr.contentWindow.document;
// 在这里操纵b.html
callback(doc);
};
}
iframe + location.hash(暂略)
原理是:利用location.hash来进行传值,a.com/a.html自动创建一个隐藏iframe为b.com/b.html的页面。
缺点是:利用hash值来进行数据传递,数据直接暴露在了url中,数据容量和类型都有限。
HTML5 postMessage
原理:页面利用html5 postMessage向iframe页面通信。
对被远程请求的页面(req.html)要求是需要加入请求方页面为iframe,并向请求方页面postMessage数据, 被请求页面监听message。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//被远程请求的页面b.com/req.html
<script type="text/javascript">
window.onload = function(){
sendPostMessage('http://a.com/index.html', {testObj: '1231231231'});
}
//url:允许请求数据的源, data:要发送的数据
function sendPostMessage(url, data){
var ifr = document.createElement('iframe');
ifr.src = url;
document.body.appendChild(ifr);
ifr.style.display = 'none';
var durl = /http:\/\/([^\/]+)\//i,
domain = url.match(durl); // domain[0]'http://b.com';
ifr.onload = function(){
ifr.contentWindow.postMessage(data, domain[0]);
};
}
</script>
然后简单封装下postMessage接收数据 main.html中接收数据1
2
3
4
5
6
7
8
9
10getPostMessage('http://b.com', function(data){console.log(data)});
//urlorigin:请求方a.com/main.html, callback,带数据的回调;
function getPostMessage(urlorigin, callback){
window.addEventListener('message', function(event){
if(event.origin === urlorigin){
callback(event.data, event.source); //对被远程请求的页面index.html中window对象的引用
}
})
}
参考列表:
JavaScript 的同源策略
HTTP访问控制(CORS)
http://f2e.me/200904/cross-scripting/