js中跨域问题详解

*什么是跨域?*
概念:只要协议,端口,域名有一个不同,都被当做不同的域;

URL—————————————–说明———————————- 是否允许通信

http://www.a.com/a.js
http://www.a.com/b.js —————域名相同————————————可以通信

http://www.a.com/lab/a.js
http://www.a.com/script/b.js ——-同一域名下的不同文件——————-可以通信

http://www.a.com:8000/a.js
http://www.a.com/b.js ————-同一域名不同端口————————–不允许通信

http://www.a.com/a.js
https://www.a.com/b.js ————-同一域名不同协议————————-不允许通信

http://www.cnblogs.com/a.js
http://www.a.com/b.js ————–不同域名———————————–不允许通信

http://www.a.com/a.js
http://20.20.20.3/b.js ————–域名和域名对应的IP——————–不允许通信

http://www.a.com/a.js
http://script.a.com/b.js ————主域名相同,子域名不同 ————–不允许通信

http://www.a.com/a.js
http://a.com/b.js ——————同一域名,不同二级域名 —————-不允许通信

需要注意的是:对于端口和协议不同的通信,只能够由后台来解决

*解决方案*
1. 夸域源资源共享(CORS)
IE对CORS的实现
IE8中引入了XDR类型,使用方法是:首先创建一个XDomainRequest实例,调用open()方法,再调用send(),XDR对象的open()方法接受两个参数:请求类型和URL。XDR请求是异步执行,请求返回后会触发load事件,相应数据也会保存在responseText属性中

var xdr = new XDomainRequest();
xdr.onload = function(){
 console.log(xdr.responseText);
}
xdr.open('get', 'http://www.baidu.com');
......
xdr.send(null);

其他浏览器对CORS的支持
其他内核为WebKit的浏览器都通过了XMLHttpRequest对象实现了对CROS的原生支持,请求另一个域中的资源时,使用标准的XHR对象并在open()方法中传入URL即可。

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
 if(xhr.readyState == 4){
  if(xhr.status >= 200 && xhr.status < 304 || xhr.status == 304){
   console.log(xhr.responseText);
  }
 }
}
xhr.open('get', 'http://www.baidu.com');
xhr.send(null);

2.JSONP
JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数,而数据就是传入回调函数中的JSON数据。

在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的

function handleResponse(response){
 //处理返回的数据;
}
<script src="http://example.com/data.php?callback=handleResponse">
</script>

JSONP的优点是:
(1)它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;
(2)它的兼容性更好,在更加古老的浏览器中都可以运行,不需XMLHttpRequest或ActiveX的支持;
(3)并且在请求完毕后可以通过调用callback的方式回传结果。
JSONP的缺点则是:
(1)安全问题(请求代码中可能存在安全隐患(请求的数很有可能有恶意代码);
(2)要确定jsonp请求是否失败并不容易

3.Web Sockets
web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。(同源策略对web sockets不适用)

web sockets原理:在JS创建了web socket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议。

var socket = new WebSockt('ws://www.baidu.com');
//http->ws; https->wss
socket.send('hello WebSockt');
socket.onmessage = function(event){
 var data = event.data;
}

当服务器向客户端发来消息时,WebSocket会触发message事件,把返回的数据保存在event.data中

4.通过修改document.domain来跨子域
浏览器都有一个同源策略,其限制之一就是不能通过ajax的方法去请求不同源中的文档。 它的第二个限制是浏览器中不同域的框架之间是不能进行js的交互操作的。

不同的框架之间是可以获取window对象的,但却无法获取相应的属性和方法。比如,有一个页面,它的地址是http://www.example.com/a.html , 在这个页面里面有一个iframe,它的src是http://example.com/b.html, 很显然,这个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的

<script type="text/javascript">
    function test(){
        var iframe = document.getElementById('ifame');
        var win = document.contentWindow;
        //可以获取到iframe里的window对象,但该window对象的属性和方法
        //几乎是不可用的
        var doc = win.document;
        //这里获取不到iframe里的document对象
        var name = win.name;
        //这里同样获取不到window对象的name属性
    }
</script>
<iframe id = "iframe" src="http://example.com/b.html" 
        onload = "test()">
</iframe>

我们只要把http://www.example.com/a.htmlhttp://example.com/b.html这两个页面的document.domain都设成相同的域名就可以了。但要注意的是,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';
    //在iframe载入这个页面也设置document.domain,
    //使之与主页面的document.domain相同
</script>

修改document.domain的方法只适用于不同子域的框架间的交互。

以上为个人总结的部分跨域解决方案,不够详尽,谢谢理解

猜你喜欢

转载自blog.csdn.net/qq_40856225/article/details/81586560