文章分享:
详解JS跨域问题
HTTP access control (CORS)
html5 postMessage解决跨域、跨窗口消息传递
跨域与跨域访问
一、什么是跨域?
概念:只要协议、域名、端口有任何一个不同,都被当作是不同的域。
二、为什么浏览器要限制跨域访问呢?
原因就是安全问题:如果一个网页可以随意地访问另外一个网站的资源,那么就有可能在客户完全不知情的情况下出现安全问题。
三、跨域的常用方法
1. 跨域资源共享(CORS)
服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin
来进行的
2. JSONP
什么是jsonp?维基百科的定义是:JSONP(JSON with Padding)
JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数,而数据就是传入回调函数中的JSON数据。
JSONP实现原理:在js中,我们直接用XMLHttpRequest请求不同域上的数据时是不可以的。但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的。
// jQuery
<script type="text/javascript">
$.getJSON('http://example.com/data.php?callback=?,function(jsondata)'){
//处理获得的json数据
});
</script>
jquery会自动生成一个全局函数来替换callback=?
中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。$.getJSON()
方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。
// 原生JavaScript
<script type="text/javascript">
function dosomething(jsondata){
//处理获得的json数据
}
</script>
<script src="http://example.com/data.php?callback=dosomething"></script>
js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。
<?php
$callback = $_GET['callback']; //得到回调函数名
$data = array('a','b','c'); //要返回的数据
echo $callback.'('.json_encode($data).')';//输出
?>
输出结果为:dosomething(['a','b','c']);
CORS和JSONP对比
CORS与JSONP相比,无疑更为先进、方便和可靠。
1、 JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
3、 JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS)。
3. 修改document.domain来跨子域
浏览器都有一个同源策略,其限制之一就是第一种方法中我们说的不能通过ajax的方法去请求不同源中的文档。 它的第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的。修改document.domain的方法只适用于不同子域的框架间的交互。
例如:我们只要把http://www.example.com/a.html 和 http://example.com/b.html这两个页面的document.domain都设成相同的域名‘example.com’
就可以了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。
1.在页面 http://www.example.com/a.html 中设置document.domain:
<iframe id = "iframe" src="http://example.com/b.html" onload = "test()"></iframe>
<script type="text/javascript">
document.domain = 'example.com';//设置成主域
function test(){
alert(document.getElementById('iframe').contentWindow);//contentWindow 可取得子窗口的 window 对象
}
</script>
2.在页面 http://example.com/b.html 中也设置document.domain:
<script type="text/javascript">
document.domain = 'example.com';
</script>
4. 通过window.name进行跨域
window.name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
name 在浏览器环境中是一个全局/window对象的属性,且当在 frame 中加载新页面时,name 的属性值依旧保持不变。通过在iframe 中加载一个资源,该目标页面将设置 frame 的 name 属性。此 name 属性值可被获取到,以访问 Web服务发送的信息。但 name 属性仅对相同域名的 frame 可访问。这意味着为了访问 name 属性,当远程 Web服务页面被加载后,必须导航 frame 回到原始域。同源策略依旧防止其他 frame 访问 name 属性。一旦 name 属性获得,销毁
frame 。
有三个页面:
- a.com/app.html:应用页面。
- a.com/proxy.html:代理文件,一般是一个没有任何内容的html文件,需要和应用页面在同一域下。
- b.com/data.html:应用页面需要获取数据的页面,可称为数据页面。
实现起来基本步骤如下:
1、数据页面会把数据附加到这个iframe的window.name上
// b.com/data.html
<script type="text/javascript">
window.name = 'I was there!'; // 这里是要传输的数据,大小一般为2M,IE和firefox下可以大至32M左右
// 数据格式可以自定义,如json、字符串
</script>
2、在应用页面(a.com/app.html)中创建一个iframe,把其src指向数据页面(b.com/data.html),并监听iframe的onload事件,在此事件中设置这个iframe的location指向本地域的代理文件(a.com/proxy.html, 代理文件和应用页面在同一域下,所以可以相互通信)
// a.com/app.html
<script type="text/javascript">
var state = 0,
iframe = document.createElement('iframe'),
loadfn = function() {
if (state === 1) {
var data = iframe.contentWindow.name; // 读取数据
alert(data); //弹出'I was there!'
}
else if (state === 0) {
state = 1;
iframe.contentWindow.location = "http://a.com/proxy.html"; // 指向代理文件
}
};
iframe.src = 'http://b.com/data.html';
if (iframe.attachEvent) {
iframe.attachEvent('onload', loadfn);
} else {
iframe.onload = loadfn;
}
document.body.appendChild(iframe);
</script>
3、获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问)。
// a.com/app.html
<script type="text/javascript">
iframe.contentWindow.document.write(''); //清空
iframe.contentWindow.close(); //销毁
document.body.removeChild(iframe); //释放内存
</script>
5. 使用HTML5的window.postMessage方法跨域
window.postMessage(data, origin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。
data
部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化origin
指明目标窗口的源,协议+主机+端口号[+URL],URL会被忽略,所以可以不写。targetOrigin设置为”*”,这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为”/”
// http://test.com/index.html
<div>
<iframe id="child" src="http://lsLib.com/lsLib.html"></iframe>
</div>
<script type="text/javascript">
window.onload = function(){
window.frames[0].postMessage('getcolor','http://lslib.com');
}
</script>
test.com/index.html 向lslib.com 发送了消息,在lslib.com 做出如下反应:
// http://lslib.com/lslib.html
<script type="text/javascript">
window.addEventListener('message',function(e){
if(e.source!=window.parent) {
return;
}
var color = container.style.backgroundColor;
window.parent.postMessage(color,'*');
},false);
</script>
MessageEvent对象是:
有几个重要属性:
- data:传递来的消息数据
- source:发送消息的窗口对象
- origin:发送消息窗口的源(协议+主机+端口号)
来源:CSDN
作者:luoxinwhu
链接:https://blog.csdn.net/luoxinwhu/article/details/77618976