七种跨域方式总结

Jsonp跨域

jsonp用一句话概括就是定义好callback(回调函数)并传送至服务端,服务端采用callback为函数名包裹json数据返回浏览器并执行。
在这里插入图片描述
flask服务器(127.0.0.1:5000)
接受回调函数名称并包裹数据返回

@app.route('/json')
def hello_world():
    cb = request.args.get("callback")
    return '{}("端口5000的消息")'.format(cb)

apache服务器(127.0.0.1:80)
script标签跨域加载端口5000的内容,定义回调函数名并传递给script标签指向的url

<script>
    function callback(data){
        alert(data)
    }
</script>

<script src="http://127.0.0.1:5000/jsonp?callback=callback"></script>

在这里插入图片描述

CORS跨域

CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 ajax 只能同源使用的限制。CORS 需要浏览器和服务器同时支持才可以生效,对于开发者来说,CORS 通信与同源的 ajax 通信没有差别,代码完全一样。浏览器一旦发现 ajax 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。

flask服务器(127.0.0.1:5000)
服务器配置CORS后可以接受ajax的跨域请求

from flask_cors import CORS
cors = CORS(app)
@app.route('/cors')
def cors_res():
    return '端口5000的消息'

apache服务器(127.0.0.1:80)

<script>
	var xhr = new XMLHttpRequest();
	xhr.onreadystatechange = function(){
		if(xhr.readyState == 4 && xhr.status == 200){
			alert(xhr.responseText);
		}
	}
	xhr.open("GET","http://127.0.0.1:5000/cors",true);
	xhr.send(null)
</script>

在这里插入图片描述
如果服务器没有配置cors,那么ajax请求会出错。
此时访问两个域响应为
flask服务器:
在这里插入图片描述
apache服务器:
在这里插入图片描述
可以看出此时请求的资源并没有响应Access-Control-Allow-Origin字段所以请求失败。

代理服务器

flask(127.0.0.1:5000)
此时后端会代替前端向www.baidu.com发起请求并把结果返回给前端

@app.route('/proxy')
def proxy():
    res = requests.get("http://www.baidu.com")
    return res.text

apache服务器(127.0.0.1:80)

<script>
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function(){
        if(xhr.readyState == 4 && xhr.status == 200){
            alert(xhr.responseText);
        }
    }
    xhr.open("GET","http://127.0.0.1:5000/proxy",true);
    xhr.send(null)
</script>

在这里插入图片描述
倘若直接请求www.baidu.com则会
在这里插入图片描述
src的标签请求也会被同源策略拦截
在这里插入图片描述

Postmessage

window.postMessage() 方法可以安全地实现跨源通信。一个窗口可以获得对另一个窗口的引用(比如 targetWindow = window.opener),然后在窗口上调用 targetWindow.postMessage() 方法分发一个 MessageEvent 消息。接收消息的窗口可以根据需要自由处理此事件。

Window.postMessage有三个参数message、targetOrigin和可选的[transfer])

  • message为发送到其他窗口的消息
  • targetOrigin为接受消息的目标窗口
  • transfer代表消息的所有权

而与之对应的message消息监听事件window.addEventListener(“message”, receiveMessage, false)

  • data为收到的数据
  • origin发送消息的域(协议://域名:端口)
  • 发送消息的窗口引用

flask服务器(127.0.0.1:5000)

@app.route('/postmessage')
def postmessage():
    return render_template('postmessage.html')

postmessage.html内容
监听当前页面收到的message并向页面顶层窗口发送消息

<body>
<p>Postmessage Test</p>
<script>
    window.addEventListener("message", function (e){
        console.log('端口5000收到消息 : ' + e.data);
        top.postMessage("端口5000发送的消息",e.origin);
    },false)
</script>
</body>

apache服务器(127.0.0.1:80)
包含iframe子窗口为跨域的页面,设置target为目标域,向目标域发送消息并弹出收到的回复。

<iframe id="chil" src="http://127.0.0.1:5000/postmessage"></iframe>
<script>
	window.onload = function() {
	    let target = "http://127.0.0.1:5000"
	    window.frames[0].postMessage('端口80发送的消息',target);
		console.log("端口80已经发出消息");
	}
	window.addEventListener('message', function(e) {
	    console.log('端口80接收到消息:', e.data);
	});
</script>

在这里插入图片描述

window.name + iframe

window.name属性代表当前窗口的名字,且每个子窗口都有自己独立的name值。而且当前页面重新加载后窗口的名字不会改变.
在这里插入图片描述
此时页面跳转到百度的界面但是window.name还是加载bing时的hello

flask服务器(127.0.0.1:5000)
每次页面加载时把当前窗口的name值设置为端口5000的数据

@app.route('/name')
def name():
    return ''' 
    <script>
        window.name = "端口5000携带的数据"
    </script>
    '''

apache服务器(127.0.0.1:80)
iframe标签包含端口5000的界面再将src设置为空或者与当前页面同源的url(否则无法操作iframe的内容),取出iframe窗口的name值完成跨域数据传输

<script>
	let data = '';
	const ifr = document.createElement('iframe');
	ifr.src = "http://127.0.0.1:5000/name";
	ifr.style.display = 'none';
	document.body.appendChild(ifr);
	ifr.onload = function() {
	    ifr.onload = function() {
	        data = ifr.contentWindow.name;
			alert('收到数据:' + data);
	    }
	    ifr.src = "about:blank;";
	}
</script>

如果不改变ifr.src的值为同域的url则会爆出如下的错误
在这里插入图片描述

location.hash + iframe

flask服务器(127.0.0.1:5000)

@app.route('/hash')
def hash():
    return render_template("hash.html")

<!-- hash.html: -->
<script>
    switch (location.hash) {
        case "#data":
            callback();
    }

    function callback() {
        try{
            parent.location.hash = "端口5000的数据";
        }catch (e) {
            console.log("尝试修改父窗口hash值失败")
            let ifr_proxy = document.createElement('iframe');
            ifr_proxy.src = "http://127.0.0.1/proxy.html#" + "端口5000的数据";
            document.body.append(ifr_proxy);
        }
    }
</script>

如果当前页面的hash值为data则触发回调函数尝试更改父窗口的hash值传递数据,由于浏览器的安全策略,页面包含的子窗口并不能直接修改父窗口的hash值,所以我们利用一个与父页面同源的iframe当做代理,代替端口5000修改端口80的hash值。

apache服务器(127.0.0.1:80)
主页面

<script>
    let ifr = document.createElement('iframe');
    ifr.src = "http://127.0.0.1:5000/hash#data"
    ifr.style.display = 'none';
    document.body.append(ifr);

    window.addEventListener('hashchange',function (e){
        alert('从端口5000获得的数据是: ',location.hash.substring(1))
    })
</script>

主页面创建跨源页面的iframe并且监听自身的hash值改变情况,如果本身的hash被改变则说明顺利收到跨源界面传来的信息。

<!--127.0.0.1/proxy.html 代理iframe-->
<script>
    parent.parent.location.hash = self.location.hash.substring(1);
</script>

代理iframe界面用来做两个跨域页面修改hash的桥梁,整个数据传递的过程如下图所示
在这里插入图片描述

document.domain降域

这种跨域方式仅限于主域相同而子域不同的情况,还是创建两个html页面,分别对应sub.example.comexample.com,修改sub.example.com的document属性为example.com就可以达到同域直接操作数据的目的。
flask服务器(127.0.0.1:5000)

@app.route('/document')
def document():
    return '''
    <script>
        document.domain = 'example.com';
        window.data = '端口5000的数据';
    </script>
    '''

flask服务器(127.0.0.1:5050)

@app.route('/crossdomain')
def hello_world():
    return render_template("cross.html")
    
<!-- cross.html-->
 <script>
     document.domain = 'example.com';
     let ifr = document.createElement('iframe');
     ifr.src = "http://sub.example.com/document";
     ifr.style.display = "none";
     document.body.append(ifr);
     ifr.onload = function(){
         let win = ifr.contentWindow;
         alert(win.data);
     }
 </script>

未设置之前的因为同源策略的错误
在这里插入图片描述
首先更改host文件的映射C:\Windows\System32\drivers\etc\host,增加

127.0.0.1    www.example.com
127.0.0.1   sub.example.com

这样每次访问example.com的页面就dns会自动解析为127.0.0.1,再配置nginx反向代理

    server {
        listen 80;
        server_name www.example.com;
        location / {
            proxy_pass http://127.0.0.1:5050/;
        }
    }

    server {
        listen 80;
        server_name sub.example.com;
        location / {
            proxy_pass http://127.0.0.1:5000/;
        }
    }

监听端口80如果有来自域名www.example.com的请求则转发到5050端口处理,如果来自sub.example.com则转发到5000端口。
最后访问www.example.com(127.0.0.1:5050)可以跨域收到5000端口的数据

在这里插入图片描述

参考文章
https://www.zhihu.com/search?type=content&q=%E8%B7%A8%E5%9F%9F
https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage
https://blog.csdn.net/u011897301/article/details/52679486

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值