什么是跨域?跨域产生原因与解决方法

一、什么是跨域?

当一个请求url的协议、域名、端口三者之间的任意一个与当前页面url不同即为跨域。

当前页面url 被请求页面url 是否跨域 原因
http://www.test.com/ http://www.test.com/index.html 同源(协议、域名、端口号相同)
http://www.test.com/ https://www.test.com/index.html 跨域 协议不同(http/https)
http://www.test.com/ http://www.baidu.com/ 跨域 主域名不同(test/baidu)
http://www.test.com/ http://blog.test.com/ 跨域 子域名不同(www/blog)
http://www.test.com:8080/ http://www.test.com:7001/ 跨域 端口号不同(8080/7001)

二、为什么会出现跨域?

出于浏览器的*同源策略限制**。

同源策略(Same Orgin Policy)是一种约定,它是浏览器核心也最基本的安全功能,它会阻止一个域的js脚本和另外一个域的内容进行交互,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。。所谓同源(即在同一个域)就是两个页面具有相同的协议(protocol)、主机(host)和端口号(port)

三、非同源限制

  • 无法读取非同源网页的cookie、localstorage和indexedDB
  • 无法接触非同源网页的DOM和js对象
  • 无法向非同源地址发送Ajax请求

四、跨域解决方法

(1)document.domain + iframe跨域
(2)location.hash + iframe跨域
(3)window.name + iframe跨域
(4)postMessage跨域
(5)通过jsonp跨域
(6)跨域资源共享(CORS)
(7)Nginx反向代理
(8)nodejs中间件地代理跨域
(9)WebSocket协议跨域

一般来说,前三个是iframe的跨域;窗口之间JS跨域postMessage;简单的跨域请求jsonp即可,复杂的用cors;开发环境下接口跨域用nginx反向代理或node中间件比较方便。

4.1、document.domain + iframe跨域

该方法仅限于主域相同,子域不同的跨域应用场景。

实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。

(1)父窗口:http://www.domain.com/a.html

<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
    document.domain = 'domain.com';
    var user = 'admin';
</script>

(2)子窗口:http://child.domain.com/b.html

<script>
    document.domain = 'domain.com';
    // 获取父窗口中变量
    alert('get js data from parent ---> ' + window.parent.user);
</script>

4.2、location.hash + iframe跨域

实现场景:A域(a.html)-> B域(b.html)-> A域(c.html)。a与b不同域只能单向通信,但c与a同域,所以c可以通过parent.parent访问a页面所有对象。

实现原理:三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

(1)a.html:http://www.domain1.com/a.html

<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>
    var iframe = document.getElementById('iframe');

    // 向b.html传hash值
    setTimeout(function() {
        iframe.src = iframe.src + '#user=admin';
    }, 1000);
    
    // 开放给同域c.html的回调方法
    function onCallback(res) {
        alert('data from c.html ---> ' + res);
    }
</script>

(2)b.html:http://www.domain2.com/b.html

<iframe id="iframe" src="http://www.domain1.com/c.html" style="display:none;"></iframe>
<script>
    var iframe = document.getElementById('iframe');

    // 监听a.html传来的hash值,再传给c.html
    window.onhashchange = function () {
        iframe.src = iframe.src + location.hash;
    };
</script>

(3)c.html:(http://www.domain1.com/c.html)

<script>
    // 监听b.html传来的hash值
    window.onhashchange = function () {
        // 再通过操作同域a.html的js回调,将结果传回
        window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', ''));
    };
</script>

4.3、window.name + iframe跨域

实现原理:通过iframe的src属性由外域转向本地域,跨域数据即由if让么的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时又是安全操作。

window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。

(1)a.html:http://www.domain1.com/a.html

var proxy = function(url, callback) {
    var state = 0;
    var iframe = document.createElement('iframe');

    // 加载跨域页面
    iframe.src = url;

    // onload事件会触发2次,第1次加载跨域页,并留存数据于window.name
    iframe.onload = function() {
        if (state === 1) {
            // 第2次onload(同域proxy页)成功后,读取同域window.name中数据
            callback(iframe.contentWindow.name);
            destoryFrame();

        } else if (state === 0) {
            // 第1次onload(跨域页)成功后,切换到同域代理页面
            iframe.contentWindow.location = 'http://www.domain1.com/proxy.html';
            state = 1;
        }
    };

    document.body.appendChild(iframe);

    // 获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问)
    function destoryFrame() {
        iframe.contentWindow.document.write('');
        iframe.contentWindow.close();
        document.body.removeChild(iframe);
    }
};

// 请求跨域b页面数据
proxy('http://www.domain2.com/b.html', function(data){
    alert(data);
});

(2)proxy.html:http://www.domain1.com/proxy....
中间代理页,与a.html同域,内容为空即可。

(3)b.html:http://www.domain2.com/b.html

<script>
    window.name = 'This is domain2 data!';
</script>

4.4、postMessage跨域

实现原理:postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可以用于解决以下方面的问题:

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

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

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

(1)a.html:http://www.domain1.com/a.html

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

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

(2)b.html:http://www.domain2.com/b.html

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

        console.log(e.source); // e.source 发送消息的窗口
        console.log(e.origin); // e.origin 消息发向的网址
        console.log(e.data);   // e.data   发送的消息

        var data = JSON.parse(e.data);
        if (data) {
            data.number = 16;

            // 处理后再发回domain1
            window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');
        }
    }, false);
</script>

4.5、通过jsonp跨域

实现原理:通常为了减轻web服务器的负载,我们把js、css、图片等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许。我们可以利用

posted @ 2020-08-17 19:47  不语微菁年,莫思菪樱  阅读(1371)  评论(0编辑  收藏  举报