JavaScript跨域

只要协议、域名、端口有任何一个不同,都被当作是不同的域。

也就是“http://www.baidu.com”这个URL首部,必须完全一样才能互相通信。

这个也叫同源策略,当前域名的js只能读取/修改同域下的窗口属性

为什么要有同源策略?为了防止CSRF攻击,保证来至不同源的对象不会互相干扰。

一、通过jsonp跨域(实际上是动态创建script标签)

原理:不同的域可以传输JS文件

服务器的data.php:

<?php

$callback=$_GET['callback'];//得到的回调函数名

$data=array('a','b','c');//需要返回的数据

echo $callback.'('.json_encode($data).')';//输出

?>

本地的HTML:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

</head>

<body>

<script>

function dosomething(jsondata){

console.log(jsondata)

}

</script>

<script src='http://www.a.com/data.php?callback=dosomething'></script>

</body>

</html>

或者用jquery:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<script src="http://cdn.static.runoob.com/libs/jquery/1.8.3/jquery.js"></script>

</head>

<body>

<script>

$.getJSON("http://www.a.com/data.php?callback=?", function(jsondata) {

console.log(jsondata);

});

</script>

</body>

</html>

结果:

["a","b","c"]

JSONP的优点是:1.它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;

         2.它的兼容性更好,在更加古老的浏览器中都 可以运行,不需要XMLHttpRequest或ActiveX的支持;

        3.在请求完毕后可以通过调用callback的方式回传结果。

JSONP的缺点是:1.它只支持GET请求而不支持POST等其它类型的HTTP请求;

        2.它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JS调用的问题。

        3.jsonp在调用失败的时候不会返回各种HTTP状态码。

        4.安全性欠缺。

2、通过document.domain+ iframe (只有在主域相同的时候才能使用该方法)

两个不同域的a.html和b.html

在www.a.com/a.html中:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>a跨域</title>

</head>

<body>

<script>

function onLoad(){

var iframe=document.getElementById('iframe');

var win=iframe.contentWindow;//能读取iframe里的window对象

var doc=win.document;//无法读取,报错

var name=win.name;//无法读取,报错

}

</script>

<iframe src="http://www.a.com/b.html" onload="onLoad()"></iframe>

</body>

</html>

在www.script.a.com/b.html中:

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>b跨域</title>

</head>

<body>

<div>内容</div>

</body>

</html>

解决办法:在两个页面都插入document.domain,这样就能访问iframe里window对象的各种属性。

<script>

document.domain='a.com';//设置成主域

</script>

3、使用window.name来进行跨域

原理:在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限

同一目录下的a.html和b.html

a.html

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>a跨域</title>

</head>

<body>

<script>

window.name='页面a设置的值';

setTimeout(function(){

window.location='b.html'

},3000);

</script>

</body>

</html>

b.html

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>b页面</title>

</head>

<body>

<script>

alert(window.name);

</script>

</body>

</html>

a.html页面载入后3秒,跳转到了b.html页面,弹出“页面a设置的值”

跨域:在b.html里设置window.name,a.html就能访问这个值了(。。。我未成功)

4、使用HTML5的window.postMessage方法来跨域传送数据

不同域的a.html和b.html

a.html

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>a跨域</title>

</head>

<body>

<script>

function onLoad(){

var iframe=document.getElementById('iframe');

var win=iframe.contentWindow;

win.postMessage('来自a页面的信息','*');//第二个参数为想访问的域,*为通配

}

</script>

<iframe src="http://www.a.com/b.html" onload="onLoad()"></iframe>

</body>

</html>

b.html

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>b跨域</title>

</head>

<body>

<script>

window.onmessage=function(e){

e=e||event;

alert(e.data);

}

</script>

</body>

</html>

5.利用flash

6.利用iframe和location.hash

7.利用CORS

CORS是自定义HTTP头部,使浏览器和服务器对比,从而决定请求和响应是否应该进行

IE8使用XDR对象实现CORS,和XHR类似用法

var xdr=new XDomainRequest();

xdr.onload=function(){

alert(xdr.responseText)

};

xdr.open('get',"http://www.a.com/");

xdr.send(null)

现代浏览器使用普通的XMLHttpRequest对象请求就行。

8.图像Ping

var img=newImage();

img.onload=img.onerror=function(){

alert('1');

};

img.src="http://www.a.com/test?name=aaa";

缺点:1.只能GET 2.无法访问服务器的响应文本

前面这几种方法,大部分都需要前后两端支持。如果单纯的前端实现跨域,可以使用代理。

1.https://github.com/hongrunhui/node-proxy(未测试过)

2.eezz.com (代理网站请求返回的数据有时候没刷新,可能是请求频率/次数受限制了?所以还是有点小问题)

  var requestUrl = "http://www.weather.com.cn/adat/sk/101110101.html";

  //这里一定要注意,实际请求的url其实是以参数形式从eezzo.com读取的,因此我们都要对url进行编码,使用encodeURI方法即可

  $.getJSON("http://eezzo.com/API/CD", { url: encodeURI(requestUrl) }, function(json) {

    console.log(json);

  });

3.雅虎代理,同上

<script type="text/javascript" src="http://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>

<script>

var requestURL="http://www.weather.com.cn/adat/sk/101110101.html";

$.getJSON("http://query.yahooapis.com/v1/public/yql", {

q: "select * from json where url=\""+requestURL+"\"", //请注意划线和两个引号

format: "json"

}, function(data) {

if (data.query.results) {

console.log(data.query.results.json);

} else {

console.log('no such code: ' + code);

}

});

</script>

4.原生代码实现jsonp跨域读取(动态创建script)

var jsonp = function (url, callback) {

if (typeof url === 'undefined') {

throw 'the 1st param "url" missing';

}

if (typeof callback === 'undefined') {

throw 'the 2nd param "callback" missing';

}

var jsonpcallback = 'callback' + new Date().valueOf();

if (typeof callback !== 'string') {

window[jsonpcallback] = callback;

callback = jsonpcallback;

} else {

window[jsonpcallback] = function (data) {

eval(callback).call(window, data);

}

}

var script = document.createElement('script');

script.setAttribute('type', 'text/javascript');

script.setAttribute('src', url + (url.indexOf('?') == -1 ? '?' : '&') + 'callback=' + jsonpcallback);

var head = document.getElementsByTagName('head')[0];

head.appendChild(script);

};

jsonp('https://api.douban.com/v2/movie/top250', function (data) {

console.log(data);

});

简化版:

        var jsonp = (url, callback) => {
            var jsonpcallback = 'callback' + new Date().valueOf(); //随便写个字符串
            window[jsonpcallback] = callback; //再把传进来的callback暴露到全局,命名为上面的字符串

            var script = document.createElement('script'); //创建标签
            script.setAttribute('src', url + (url.indexOf('?') == -1 ? '?' : '&') + 'callback=' + jsonpcallback);
            document.getElementsByTagName('head')[0].appendChild(script); //标签插入头部
        };

5.设置dataType为jsonp

$.ajax({

url: "https://api.douban.com/v2/movie/top250",

dataType: 'jsonp',

success: function (data) {

console.log(data);

}

});