彻底明白前端常用的几种跨域方式

说到跨域,首先要理解什么是跨域,浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域。

一.跨域基础知识

http://www.a.com:8080/a.html

http://为协议,www为子域名,a.com为主域名,8080为端口,a.html为资源地址,当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算作“跨域”。跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了,浏览器认为这不安全,所以拦截了响应。知道了什么是跨域,那么我们先要本地搭建个服务器,用wampserver或者xampp都可以,这里我用的是xampp,详情点击这里

二.跨域方案

1.jsonp

原理:利用 script 标签没有跨域限制的漏洞,例如 <script src=“http:///www.jsonp.com/test.php?callback=test” /script>前端会先定义一个test函数,后端会获取到callback后面这个test函数名,然后把前端需要的参数放到test函数里面并执行

准备两个文件(index.html,test.php),两个域名(www.test.com,www.jsonp.com)
index.html 放在www.test.com域名下,test.php放在www.jsonp.com域名下!

下面我们来看下简单的例子,,用ajax来测试一下

http://www.test.com/index.html

$.ajax({
    type: "GET",
    url: "http://www.jsonp.com/test.php",
    dataType: "jsonp",
    jsonp: "callback",
    jsonpCallback: "test",
    success: function (data) {
        console.log(typeof data, data);
        //object,{name: "zhangsan", age: 18}
        console.log(`my name is ${data.name},my age is ${data.age}`);
        //my name is zhangsan,my age is 18
    }
});

http://www.jsonp.com/test.php

<?php
$data = array(
    'name' => 'zhangsan',
    'age' => 18,
);
$callback = $_GET['callback'];
echo $callback."(".json_encode($data).")";
?>

当dataType为jsonp,ajax默认会创建一个script标签,并把jsonp和 jsonpCallback参数拼接到script标签中src,然后并把script标签插入到head中

let script = document.createElement('script');
script.src = 'http://www.jsonp.com/test.php?callback=test';
script.type="text/javascript";
document.getElementsByTagName('head')[0].appendChild(script)

看下图:域名www.test.com去请求www.domain.com的数据,明显跨域
在这里插入图片描述

在这里插入图片描述

jsonp缺点:

  1. 只能发送get请求,不支持post put delete等方式
  2. 不安全,去请求别的域,得到别的域返回的数据,万一是个脚本,那就JJ了。

2.location.hash+iframe

原理:利用hash值进行通讯,例如:有三个页面a.html,b.html,c.html, a.html和c.html是同域的,一开始a.html给b.html传一个hash值,然后b.html收到hash值后,再把hash值传递给c.html,最后c.html将传过来的值传到a.html中(a.html和c.html是同域的)。

准备3个文件(a.html,b.html,chtml) 两个域名(www.test.com,www.hash.com)
a.html,c.html放在www.hash.com域名下,b.html放在www.test.com域名下

www.hash.com和www.test.com是不同域的!下面看a,b,c三个页面分别做了什么?


http://www.hash.com/a.html

<body>
 <iframe id="iframe" src="http://www.test.com/b.html" style="display:none;"></iframe>
<script>
    var iframe = document.getElementById('iframe');
    // 向b.html传hash值
    iframe.onload = function () {
        iframe.src = iframe.src + '#我是a页面传过来的值';
    };
    // 给c.html的回调方法
    function onCallback(res) {
        console.log(decodeURIComponent(res).slice(1));
    }
</script>
</body>

http://www.test.com/b.html

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

    // 监听a.html传来的hash值,再传给c.html
    window.onhashchange = function () {
        //输出a页面传过来的值
        console.log(decodeURIComponent(location.hash).slice(1));
        //b页面需要传给a页面的值,先传给c页面
        iframe.src = iframe.src + "#我是b页面传过来的值";
    };
</script>
</body>

http://www.hash.com/c.html

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

在这里插入图片描述
访问a会加载b,通过b的iframe的hash传值,然后b载入后,会加载c.html,并通过c的iframe的hash传值,而a和c是同域的,这样a就得到了c的值,所以就间接实现了a和b不同域的传值!


3.document.domain+iframe

原理:利用document.domain设置成自身或更高一级的父域,两者主域相同了就可以互通数据了(限制条件:主域必须相同才可以使用这种方式跨域)

准备两个文件a.html,b.html 主域相同子域不同的两个域名(a.domain.com,b.domain.com)
a.html放到a.domain.com域名下,b.html 放到b.domain.com域名下!


http://a.domain.com/a.html

<body>
    <iframe src="http://b.domain.com/b.html" id="iframe" style="display:none;"></iframe>
    <script>
        document.domain = "domain.com";
        var iframe = document.getElementById('iframe');
        iframe.onload = function () {
            console.log(iframe.contentWindow.value);
        }
    </script>
</body>

http://b.domain.com/b.html

<body>
    <script>
        document.domain = "domain.com";
        var value = 100;
    </script>
</body>

当a文件加载b文件的后,可以获取到b文件里面的数据,当主域之间想相互通信的时候,只要将两者的document.domain赋值为当前的主域名,即可实现跨域通信。


4.window.name+iframe

原理: window.name一直会存在当前窗口中,即时有新页面载入,window.name也不会变化,利用这点可以实现不同域名之前的跨域(需要一个代理页面作中转)

准备3个文件(a.html,b.html,proxy.html), 两个域名(www.test.com,www.name.com)
a.html,proxy放到www.test.com域名下,b.html放到www.name.com域名下


http://www.name.com/a.html

<body>
    <iframe id="iframe" src="http://www.name.com/b.html" style="display: none;"></iframe>
    <script>
        let iframe = document.getElementById('iframe'),falg = true;
        //这方法会执行两次,第一次是加载 src="http://www.name.com/b.html" 第二次加载是src = "proxy.html",第二次加载可以拿到第一次加载的winodw.name
        iframe.onload = () =>  falg ? (iframe.src = "proxy.html", falg = false) : console.log(iframe.contentWindow.name);
    </script>
</body>

http://www.test.com/b.html

<body>
    <script>
        window.name = "b页面传过来的window.name";
    </script>
</body>

在这里插入图片描述
a.html加载iframe时候就会去加载b.html,b.html设置window.name,然后又会去加载c.html,那么c.html就能拿到这个winodw.name, 因为a.html,c.html同域,所以可以互通数据!window.name可以存储不超过2M的数据,传递的数据都会变成string类型。

5.postMessage+iframe

介绍:postMessage是html为了解决跨域通信,特别引入的一个新的API,用来解决跨页面通信,或者通过iframe嵌套的不同页面的通信的。postMessage为发送方,onmessage为接收方。


接受方:

otherWindow.postMessage(message, targetOrigin);
  1. otherWindow为接收方的window对象,window.open()方法返回的值就是打开页面的window对象,frame元素的contentWindow属性能获得iframe标签内页面的window对象。
  2. messag为所发送的内容,可以是字符串也可以是任何javascript对象。
  3. targetOrigin是接收方域,可指定该值(一般为接收方页面的window.origin),如果接收方窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送。该参数也可以是‘*’,表示对接收方的域没有任何限制。

发送方:

window.addEventListener(“message”, function(e){},false);
  1. 函数参数e为message实例,里面包含了data、origin、source等属性,data是发送方发送的message,origin是发送方所属的域,source是发送方的window对象的引用。
  2. 第三个参数默认是false,冒泡阶段执行,如果为true是捕获阶段执行。
  3. 也可以写成 window.onmessage = function(e){ }

准备两个文件a.html,message.html,两个域名(www.test.com,www.postmessage.com)
a.html放到www.postmessage.com域名下,message.html放到www.test.com域名下


http://www.postmessage.com/a.html

<body>
<input type="button" value="点击发送数据" onclick="sendData();" />
<iframe src="http://www.test.com/message.html" id="iframe"></iframe>
<script>
    let sendData = () => {
        let iframe = document.getElementById('iframe'),
            str = "我是发送的数据";
        iframe.contentWindow.postMessage(str, 'http://www.test.com/message.html');
    };
    //监听message事件   
    window.addEventListener('message', () => {
        if (event.origin !== 'http://www.test.com') return;
        console.log(event.data);
    }, false);
</script>
</body>

http://www.test.com/message.html

<body onload="receiveData();">
<p>message</p>
<script>
    function receiveData() {
        //监听message事件   
        window.addEventListener("message", (ev) => {
            if (ev.origin != "http://www.postmessage.com") {
                console.log("the event doesn't come from Domain1!");
                return;
            }
            console.log(ev.data);
            event.source.postMessage('我是返回的数据', event.origin);
        });
    };
</script>
</body>

在这里插入图片描述


还有2种跨域方式cors和webSocket,到时候我再补上!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值