JS 跨域详解


跨域与同源

跨域

跨域是指从一个域名的网页去请求另一个域名的资源。比如从 www.baidu.com 页面去请求 www.google.com 的资源。
非同源,在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。它是由浏览器的同源策略造成的,是浏览器对JavaScript施加的安全限制

同源策略

同源策略是浏览器最核心也最基本的安全功能。如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

  • 同源:协议、域名、端口,三者全部相同,才是同源。
  • 跨域:协议、域名、端口,只要有一个的不同,就是跨域。

http://www.123.com/index.html 调用 http://www.123.com/server.php (非跨域)
http://www.123.com/index.html 调用 http://www.456.com/server.php (主域名不同:123/456,跨域)
http://abc.123.com/index.html 调用 http://def.123.com/server.php (子域名不同:abc/def,跨域)
http://www.123.com:8080/index.html 调用 http://www.123.com:8081/server.php (端口不同:8080/8081,跨域)
http://www.123.com/index.html 调用 https://www.123.com/server.php (协议不同:http/https,跨域)
请注意:localhost127.0.0.1 虽然都指向本机,但也属于跨域。

不存在跨域的情况(无视同源策略)

  1. 服务端请求服务端不存在跨域(浏览器请求服务器才存在同源策略)
  2. <img src="跨域的图片地址"><img>标签的 src 属性不存在跨域)
  3. <link href="跨域的css地址"><link>标签的 href 属性不存在跨域)
  4. <script src="跨域的js地址"></script><script>标签的 src 属性不存在跨域)

跨域常见的几种方法

1. jsonp 跨域

jsonp是一种跨域通信的手段,它的原理其实很简单:

利用<script>标签的src属性跨域

由于使用script标签的src属性,因此只支持get方法

function jsonp(req){
	// 动态创建script标签
    var script = document.createElement('script');
    var url = req.url + '?callback=' + req.callback.name;
    script.src = url;
    document.getElementsByTagName('head')[0].appendChild(script);
}

// 调用
jsonp({
    url : '',
    callback : hello 
});

当然,jquery也支持jsonp的实现方式:

$.ajax({
    url:'http://www.xxx.com',
    type:'GET',
    dataType:'jsonp',// 请求方式为jsonp
    jsonpCallback:'callback',
    data:{
        "username":"xxx"
    }
})

2. document.domain + iframe 跨域

两个域名必须属于同一个主域名!而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域。

什么是主域名相同呢?

  • www.baidu.com(百度官网)
  • map.baidu.com(百度地图)
  • tieba.baidu.com(百度贴吧)
  • news.baidu.com(百度新闻)
  • image.baidu.com(百度图片)

这五个主域名都是baidu.com

.

举个例子,现在有两个页面:

  1. news.baidu.com(news.html)
  2. map.baidu.com(map.html)

news.baidu.com里的一个网页(news.html)引入了map.baidu.com里的一个网页(map.html)

这时news.html里同样是不能操作map.html里面的内容的。
因为document.domain不一样,一个是news.baidu.com,另一个是map.baidu.com。
这时我们就可以通过Javascript,将两个页面的domain改成一样的
需要在a.html里与b.html里都加入:

document.domain = “baidu.com”;

这样这两个页面就可以互相操作了。也就是实现了同一一级域名之间的"跨域"。

具体代码:

<!-- news.baidu.com下的news.html页面 -->
<script>
    document.domain = 'baidu.com';
    var ifr = document.createElement('iframe');
    ifr.src = 'map.baidu.com/map.html';
    ifr.style.display = 'none';
    document.body.appendChild(ifr);
    ifr.onload = function(){
        var doc = ifr.contentDocument || ifr.contentWindow.document;
        // 这里可以跨域操作map.baidu.com下的map.html页面
        var oUl = doc.getElementById('ul1');
        alert(oUl.innerHTML);
        ifr.onload = null;
    };
</script>
<!-- map.baidu.com下的map.html页面 -->
<ul id="ul1">我是map.baidu.com中的ul</ul>
<script>
    document.domain = 'baidu.com';
</script>

3. postMessage跨域

在HTML5中新增了postMessage方法,postMessage可以实现跨文档消息传输(Cross Document Messaging)
Internet Explorer 8, Firefox 3, Opera 9, Chrome 3和 Safari 4都支持postMessage

postMessage可以解决:

  • 页面和其打开的新窗口的数据传递
  • 多窗口之间消息传递
  • 页面与嵌套的iframe消息传递
  • 上面三个场景的跨域数据传递

postMessage(data,origin)方法接受两个参数:

  • data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
  • origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

.

举个栗子
a.html (www.baidu.com/a.html)

<iframe id="iframe" src="http://www.qq.com/b.html" style="display:none;"></iframe>
<script>       
    var iframe = document.getElementById('iframe');
    iframe.onload = function() {
        var data = {
            name: 'xxx'
        };
        // 向qq.com传送跨域数据
        iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.qq.com');
    };

    // 接受由qq.com返回数据
    window.addEventListener('message', function(e) {
        alert('data from qq ---> ' + e.data);
    }, false);
</script>

b.html (www.qq.com/b.html)

<script>
    // 接收由baidu.com返回的数据
    window.addEventListener('message', function(e) {
        alert('data from baidu ---> ' + e.data);

        var data = JSON.parse(e.data);
        if (data) {
            data.number = 16;
            // 处理后再发回baidu.com
            window.parent.postMessage(JSON.stringify(data), 'http://www.baidu.com');
        }
    }, false);
</script>

4. CORS 跨域资源共享

是目前主流的跨域解决方案

CORS 跨域资源共享传送门


5. node代理跨域

跨域行为是浏览器禁止的,但是服务端并不禁止。使用proxyTable的原理就是将域名发送给本地的node服务器,再由本地的服务器去请求真正的服务器。

以vue为例

config/index.js文件中

proxyTable: {
	'/api': {
		// 测试环境
		target: 'http://baidu.com',  // 接口域名  要代理的url
		changeOrigin: true,  //是否跨域
		pathRewrite: {
		    '^/api': ''
	}              
}

配置完必须要重启node服务才会生效!!!

posted @ 2022-07-20 18:15  猫老板的豆  阅读(2264)  评论(0编辑  收藏  举报