1.JavaScript跨域原因--同源策略


在客户端编程语言中,如javascript和 ActionScript,同源策略是一个很重要的安全理念,它在保证数据的安全性方面有着重要的意义。同源策略规定跨域之间的脚本是隔离的,一个域的脚本不能访问和操作另外一个域的绝大部分属性和方法。那么什么叫相同域,什么叫不同的域呢?当两个域具有相同的协议(如http), 相同的端口(如80),相同的host(如www.example.org),那么我们就可以认为它们是相同的域。比如 http://www.example.org/index....http://www.example.org/sub/in...是同域,而http://www.example.org, https://www.example.org, http://www.example.org:8080, http://sub.example.org中的任何两个都将构成跨域。同源策略还应该对一些特殊情况做处理,比如限制file协议下脚本的访问权限。本地的HTML文件在浏览器中是通过file协议打开的,如果脚本能通过file协议访问到硬盘上其它任意文件,就会出现安全隐患,目前IE8还有这样的隐患。

由于JavaScript同源策略的限制,跨域资源共享就会受到制约:a.com域名下的js无法访问b.com或c.a.com下的对象,虽然保证了安全,但是给注入iframe和AJAX应用带来麻烦。跨域的限制具体表现为:
图片描述

注意:只有协议、域名、端口号完全一样才是同一域,其他情况,即使是相对应的IP和域名也是不同域。

解决跨域问题的方式

单向跨域 JSONP、window.name、server proxy

双向跨域 document.domain、window.postMessage

后端方法

https://developer.yahoo.com/j...

server proxy

在数据提供方没有提供对JSONP协议或者 window.name协议的支持,也没有对其它域开放访问权限时,我们可以通过server proxy的方式来抓取数据。例如当www.a.com域下的页面需要请求www.b.com下的资源文件asset.txt时,直接发送一个指向 www.b.com/asset.txt的Ajax请求肯定是会被浏览器阻止。这时,我们在www.a.com下配一个代理,然后把Ajax请求绑定到这个代理路径下,例如www.a.com/proxy/, 然后这个代理发送HTTP请求访问www.b.com下的asset.txt,跨域的HTTP请求是在服务器端进行的,客户端并没有产生跨域的Ajax请求。这个跨域方式不需要和目标资源签订协议,带有侵略性,另外需要注意的是实践中应该对这个代理实施一定程度的保护,比如限制他人使用或者使用频率。

前端方法:

方法1 document.domain+iframe

适用于:主域相同而子域不同(二级域名相同、协议相同、端口相同)。
具体的做法是可以在http://www.a.com/a.htmlhttp://script.a.com/b.html两个文件中分别加上document.domain = ‘a.com’;然后通过a.html文件中创建一个iframe,去控制iframe的contentDocument,这样两个js文件之间就可以“交互”了。

    
    //www.a.com上的a.html
    document.domain = 'a.com';
    var ifr = document.createElement('iframe');//创建子框架
    ifr.src = 'http://script.a.com/b.html'; //子框架指向b.html
    ifr.style.display = 'none';
    document.body.appendChild(ifr);
    ifr.onload = function(){
        //定义指针指向子框架内容的文档对象, 跨浏览器兼容
        var doc = ifr.contentDocument || ifr.contentWindow.document;
        // 获得子框架的文档对象之后在这里操纵b.html
        alert(doc.getElementsByTagName("h1")[0].childNodes[0].nodeValue);
    };

注意:除IE外的四大浏览器支持contentDocument属性,IE8以下只支持contentWindow对象,这个对象具有document属性,这个属性的效果和contentDocument属性一致。

    //script.a.com上的b.html
    document.domain = 'a.com';

问题:

1、安全性,当一个站点(b.a.com)被攻击后,另一个站点(c.a.com)会引起安全漏洞。
2、如果一个页面中引入多个iframe,要想能够操作所有iframe,必须都得设置相同domain。

方法2: 跨文档消息传递 cross-document messaging,XDM(HTML5新增)

适用:这个方法非常强大,无视协议,端口,域名的不同。

otherWindow.postMessage(message, targetOrigin);
参数: otherWindow: 对接收信息页面的window的引用。可以是:页面中iframe的 contentWindow属性;window.open的返回值;通过name或下标从window.frames取到的值。

message: 所要发送的数据,string类型。
targetOrigin: 用于限制otherWindow,“*”表示不作限制

a.com/index.html中的代码:

<iframe id="ifr" src="b.com/index.html"></iframe>
<script type="text/javascript">
window.onload = function() {
    var ifr = document.getElementById('ifr');
    var targetOrigin = 'http://b.com';  // 若写成'http://b.com/c/proxy.html'效果一样
                                // 若写成'http://c.com'就不会执行postMessage了
    ifr.contentWindow.postMessage('I was there!', targetOrigin);
};
</script>

b.com/index.html中的代码:

<script type="text/javascript">
    window.addEventListener('message', function(event){
        // 通过origin属性判断消息来源地址
        if (event.origin == 'http://a.com') {
            alert(event.data);    // 弹出"I was there!"
            alert(event.source);  // 对a.com、index.html中window对象的引用
                                  // 但由于同源策略,这里event.source不可以访问window对象
        }
    }, false);
</script>

方法3 JSONP(JSON with padding)

原理:使用<script>标签不受限制的从其他域加载资源,使用时为其src属性指定一个跨域URL。因为JSONP是有效的JavaScript代码,加载完成后就会执行。
JSONP 由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数。回调函数的名字一般是在请求中指定的。而数据就是传入回调函数中的 JSON 数据。下面是一个典型的 JSONP请求。http://freegeoip.net/json/?ca...

缺点:恶意代码注入,并且难以判断JSONP是否成功运行,一般使用定时器检测指定时间内是否接收到了响应。

function handleResponse(response){
alert("You’ re at IP address " + response.ip + ", which is in " +
response.city + ", " + response.region_name);
}

var script = document.createElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script, document.body.firstChild);

方法4: window.name

window.name = "My window's name";
location.href = "http://www.qq.com/";
//再检测 window.name :
window.name; // My window's name

可以看到,如果在一个标签里面跳转网页的话,我们的 window.name 是不会改变的。基于这个思想,我们可以在某个页面设置好 window.name 的值,然后跳转到另外一个页面。在这个页面中就可以获取到我们刚刚设置的 window.name 了。
优点:这种方法与 document.domain 方法相比,放宽了域名后缀要相同的限制,可以从任意页面获取 string 类型的数据。

方法5 flash

jQuery中的ajax:

$.ajax({
                async: false,
                url: "http://192.168.0.5/Web/web1.aspx",
                type: "GET",
                dataType: 'jsonp',
                //jsonp的值自定义,如果使用jsoncallback,那么服务器端,要返回一个jsoncallback的值对应的对象.
                jsonp: 'jsoncallback',
                //要传递的参数,没有传参时,也一定要写上
                  data: null,
                timeout: 5000,
                //返回Json类型
                  contentType: "application/json;utf-8",
                //服务器段返回的对象包含name,data属性.
                success: function (result) {
                    alert(result.date);
                },
                error: function (jqXHR, textStatus, errorThrown) {
                    alert(textStatus);
                }
            });
实际上,在我们执行这段js时,js向服务器发出了这样一个请求:
http://192.168.0.5/Web/web1.aspx?jsoncallback=jsonp1354505244726&_=1354505244742
而服务器也相应的返回了如下对象:
jsonp1354506338864({"name":"zhangsan","date":"2012-12-03"})

此时就实现了跨域范文数据的要求.


zhangding
358 声望23 粉丝

JavaScript+React+Redux