几种解决js跨域的方法
js的跨域:由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一
与当前页面地址不同即为跨域。如下示例:
URL 说明 是否允许通信
http://www.a.com/b.js 同一域名下 允许
http://www.a.com/script/b.js 同一域名下不同文件夹 允许
http://www.a.com/b.js 同一域名,不同端口 不允许
https://www.a.com/b.js 同一域名,不同协议 不允许
http://70.32.92.74/b.js 域名和域名对应ip 不允许
http://script.a.com/b.js 主域相同,子域不同 不允许
http://a.com/b.js 同一域名,不同二级域名(同上) 不允许(cookie这种情况下也不
允许访问)
http://www.a.com/b.js 不同域名 不允许
注意:对于端口和协议的不同,只能通过后台来解决。
解决办法:
1.script标签的形式:jsonp
2.document.domain
3.服务器代理 4.window.name
5.flash
6. html5 postMessage
7. iframe和location.hash
通过jsonp
在js中,我们直接用ajax中XMLHttpRequest对象请求不同域上的数据时,是不可以的,
不能跨域,但是,在页面上引入不同域上的js脚本文件是可以的,jsonp就是利用这个特
性来实现的。
例如,如果有个a.html页面,它里面的代码需要利用ajax获取一个不同域上的json数据
,那么a.html中的代码就可以这样写:
script type="text/javascript">
function jonpCallBack(data){
}
</script>
<script type="text/javascript" src="http://example.com/data.php?
callback=jonpCallBack"></script>
或者自动创建js加入页面:
function createJs(sUrl){
var oScript = document.createElement(‘script‘);
oScript.type=‘text/javascript‘;
oScript.src = sUrl;
document.getElementsByTagName(‘head‘)[0].appentChild(oScript);
}
createJs(‘http://example.com/data.php?callback=jonpCallBack‘);
js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作
为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。
php代码:
<?php
$callback = $_GET[‘callback‘];//得到回调函数名
$data = array(‘a‘,‘b‘,‘c‘);//要返回的数据
echo $callback.‘(‘.json_encode($data).‘)‘;//输出
?>
最终,输出结果为:jonpCallBack([‘a’,’b’,’c’]);
如果使用jQuery,通过它的封装方法就能很方便的来才操作jsonp了。
<script type="text/javascript">
$.getJSON(‘http://example.com/data.php?callback=?,function(jsondata)‘){
//处理获得的json数据
});
</script>
jquery会自动生成一个全局函数来替换callback=?中的问号,之后获取到数据后又会自
动销毁,实际上就是起一个临时代理函数的作用。$.getJSON方法会自动判断是否跨域,
不跨域的话,就调用普通的ajax方法;跨域的话,则会以异步加载js文件的形式来调用
jsonp的回调函数。
利用iframe和location.hash:
这个方法也可以解决完全跨域情况下的问题,利用location.hash来进行传值。在url:
http://a.com#helloword中的‘#helloworld’就是location.hash,改变hash并不会导
致页面刷新,所以可以利用hash值来进行数据传递,当然数据容量是有限的。假设域名
a.com下的文件cs1.html要和cnblogs.com域名下的cs2.html传递信息,cs1.html首先创
建自动创建一个隐藏的iframe,iframe的src指向cnblogs.com域名下的cs2.html页面,
这时的hash值可以做参数传递用。cs2.html响应请求后再将通过修改cs1.html的hash值
来传递数据(由于两个页面不在同一个域下IE、Chrome不允许修改
parent.location.hash的值,所以要借助于a.com域名下的一个代理iframe;Firefox可
以修改)。同时在cs1.html上加一个定时器,隔一段时间来判断location.hash的值有没
有变化,一点有变化则获取获取hash值。代码如下:
先是a.com下的文件cs1.html文件:
function startRequest(){
var ifr = document.createElement(‘iframe‘);
ifr.style.display = ‘none‘;
ifr.src = ‘http://www.cnblogs.com/lab/cscript/cs2.html#paramdo‘;
document.body.appendChild(ifr);
}
function checkHash() {
try {
var data = location.hash ? location.hash.substring(1) : ‘‘;
if (console.log) {
console.log(‘Now the data is ‘+data);
}
} catch(e) {};
}
setInterval(checkHash, 2000);
cnblogs.com域名下的cs2.html:
//模拟一个简单的参数处理操作
switch(location.hash){
case ‘#paramdo‘:
callBack();
break;
case ‘#paramset‘:
//do something……
break;
}
function callBack(){
try {
parent.location.hash = ‘somedata‘;
} catch (e) {
// ie、chrome的安全机制无法修改parent.location.hash,
// 所以要利用一个中间的cnblogs域下的代理iframe
var ifrproxy = document.createElement(‘iframe‘);
ifrproxy.style.display = ‘none‘;
ifrproxy.src = ‘http://a.com/test/cscript/cs3.html#somedata‘; //
注意该文件在"a.com"域下
document.body.appendChild(ifrproxy);
}
}
a.com下的域名cs3.html
//因为parent.parent和自身属于同一个域,所以可以改变其location.hash的值
parent.parent.location.hash = self.location.hash.substring(1);
当然这样做也存在很多缺点,诸如数据直接暴露在了url中,数据容量和类型都有限等