跨域实现详解

一直觉得jsonp很好用,却不知道原理,跨越技术有点多,在这探索解释一番,望有所获


同源策略

在JavaScript中,有一个很重要的安全性限制,被称为“Same-Origin Policy”(同源策略)。这一策略对于JavaScript代码能够访问的页面内容做了很重要的限制,即JavaScript只能访问与包含它的文档在同一域下的内容。(图片来自MDN)

这里写图片描述


JSONP

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

  • jsonp的客户端实现
    tocat服务器上有个remote.js(这里用tocat,web项目演示)
    这里写图片描述
alert("跨域成功");

本地index.html文件

<!DOCTYPE>
<html>
<head>
    <title>跨域资源</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    /* 完成跨域 */
    <script src="http://localhost:8080/web/js/remote.js"></script>
</head>
<body>
</body>
</html>

这里写图片描述
很明显,显示跨域成功

  • 接下来我们在本地index.html定义callback函数 ,然后远程tomcat.js传入回调数据,
    index.html
<!DOCTYPE>
<html>
<head>
    <title>跨域资源</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    /* 完成跨域 */
     <script type="text/javascript">
         window.onload = function() {
         var callback = function(data) {
            alert("跨域调用远程tomcat.js成功: " + data.name);
        }
    }
    </script>
    <script src="http://localhost:8080/web/js/tomcat.js"></script>
</head>
<body>
</body>
</html>

远程服务器:
tomcat.js

callback({"name":"kk"});

到此,跨域基本实现,但是这里有个问题,远程站点怎么知道要调用哪个callback呢?
所以就有了jsonp的核心:服务端提供的callback是动态生成的, 本地只要动态传一个callback给服务端,服务端就知道该调用哪个了
像这样:
客户端

<!DOCTYPE>
<html>
<head>
    <title>跨域资源</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
    <script type="text/javascript">
    window.onload = function() {
        var callback = function(data) {
            alert("跨域调用远程成功: " + data.name);
        }
        var url = "http://localhost:8080/web/js/helloServlet?userid=1&callback=callback";
        // 创建script标签,设置其属性
        var script = document.createElement('script');
        script.setAttribute('src', url);
        // 把script标签加入head,此时调用开始
        document.getElementsByTagName('head')[0].appendChild(script);
    }
    </script>
</head>
<body>
</body>
</html>

服务端:(这里选用java解释)

 // 从客户端获取参数userid和callback
	PrintWriter out =response.getWriter();
// 根据userid查找到的数据存储为json,
	out.println(callback+"("+json+")");
// 返回给客户端的代码,callback(json)

这里写图片描述

jsonp的执行全过程就是这么回事了(个人理解)

  • 现在再来看JQuery中的实现,我们最经常用的$.ajax,似乎通透了不少,也就是这么回事
    $(function() {
        $.ajax({
            async: false,
            url: http, //跨域url地址 
            type: "GET",
            dataType: 'jsonp',
            jsonp: 'jsoncallback',
            data: {
                "userid": "1"
            },
            timeout: 5000,
            beforeSend: function() {
                //jsonp 方式此方法不被触发.原因可能是dataType如果指定为jsonp的话,就已经不是ajax事件了  
            },
            success: function(json) { //客户端jquery预先定义好的callback函数,成功获取跨域服务器上的json数据后,会动态执行这个callback函数  
           
            }
           
            error: function(xhr) {
                //jsonp 方式此方法不被触发.原因是:dataType如果指定为jsonp的话,就已经不是ajax事件了  
                //请求出错处理  
                alert("请求出错(请检查相关度网络状况.)");
            }
        });
    })

这里发现,$.ajaxajax并不是一回事啊,ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加


window.name

注意,window.name的值只能是字符串的形式,这个字符串的大小最大能允许2M左右甚至更大的一个容量,具体取决于不同的浏览器,但一般是够用了。
实例:
假设有三个页面:

http://a.com/a.html  //渴望获得数据的页面
http://a.com/b.html  //a的兄弟页面,充当中间人角色
http://b.com/data.html //不同源的数据页
  • http://a.com/a.html,监听iframe的onload事件,在此事件中设置这个iframe的src指向本地域的代理页面http://a.com/b.html(代理文件和应用页面在同一域下,所以可以相互通信)
    动态创建iframe
    <script type="text/javascript">

    var state = 0,
        iframe = document.createElement('iframe');
        
    // 通过iframe的src与data.html通信
    iframe.src = 'http://b.com/data.html';
    
    //监听onload
    if (iframe.attachEvent) {
        iframe.attachEvent('onload', loadfn);
    } else {
        iframe.onload = loadfn;

    var loadfn = function() {
            if (state === 0) {
                state = 1;
                iframe.contentWindow.location = "http://a.com/b.html"; // 设置的代理页面
            }
            else if (state === 1) {
                var data = iframe.contentWindow.name; // 读取数据
                alert(data); //弹出'data.html!'
            } 
        };
    </script>
  • http://b.com/data.html
    <script type="text/javascript">
    	window.name = 'data.html';
    </script>

这样就可以成功解决了,理一下思路,应该是这样:
充当中间人的iframe想要获取到data.html的通过window.name设置的数据,只需要把这个iframe的src设为www.cnblogs.com/data.html就行了。然后a.html想要得到iframe所获取到的数据,也就是想要得到iframe的window.name的值,还必须把这个iframe的src设成跟a.html页面同一个域才行
图示:
这里写图片描述


HTML5中新引进的window.postMessage方法

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

实现:
发送信息的页面http://a.com/a.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>客户端</title>
    <script type="text/javascript">
    window.onload = function() {
        window.frames[0].postMessage('data', 'http://b.com/data.html');
        //postMessage接收两个参数,第一个数据,第二个字符串参数,指明目标窗口的源,具体看MDN
    }
    </script>
</head>

<body>
    <iframe id="child" src="http://b.com/data.html"></iframe>
</body>

</html>

接收信息的页面http://b.com/data.html

// 通过监听message事件获取数据
window.addEventListener('message',function(e){
                var e =e||event;
                alert(e.data);
            },false);

图示:
这里写图片描述


跨域资源共享(CORS)

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

**因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。**具体原理参见MDN

  • 当然:还有document.domain…
  • 以上十个人理解,ps: 欢迎指正
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值