前端跨域的那些事

这一节,咱们来说一讲,前端跨域的那些事,主要分红这样的几部分来说解,javascript

1、为何要跨域?php

2、常见的几种跨域与使用场景css

  2.1 JSONP跨域html

  2.2 iframe跨域前端

  2.3 window.name 跨域java

  2.4 document.domain 跨域jquery

  2.5 cookie跨域ajax

  2.6 postMessage跨域json

3、总结后端

1、为何要跨域

 跨域,一般状况下是说在两个不经过的域名下面没法进行正常的通讯,或者说是没法获取其余域名下面的数据,这个主要的缘由是,浏览器出于安全问题的考虑,采用了同源策略,经过浏览器对JS的限制,防止恶意用户获取非法的数据。好比这样的一个场景,恶意用户仿造一个银行的官网,在用户输入框中嵌套了银行的页面,若是是没有同源策略的限制,那么恶意用户则能够经过这样的一种方法来获取银行用户的卡号和登陆密码,这样对于浏览器来讲是没有安全性可言的。同时也能够有效的规避了大部分的XSS攻击(XSS攻击原理:经过向用户界面中注入script脚本,而后在脚本中获取用户的token等身份信息,而后将身份信息发送到恶意用户指定的地方,在正经常使用户尚未推出的时候,也就是token等身份信息还有效的时候,经过这些信息强制登陆,将正经常使用户挤下系统。)

 

2、常见的几种跨域与使用场景

前端的跨域主要有:JSONP跨域、iframe跨域、window.name 跨域、document.domain 跨域、cookie跨域、postMessage跨域  后端的跨域:http配置

这里咱们就主要说明一下前端的跨域,后端的跨域方法不作说明:

跨域方式 特色 局限
JSONP跨域 一、JSONP跨域不是一种前端技术,而是程序猿创造的一种跨域方法
二、JSONP跨域,是一种简单的跨域方法,兼容性比较好
 
iframe 跨域 一、操做简便 二、兼容性好 单纯的使用iframe跨域没法获取其余域名下的数据
window.name 跨域 一、必须与iframe配合使用 二、能够获取其余域名下的数据  
document.domain 跨域 一、必须保证两个要跨域的对象是有一个关联域名   一、只针对两个跨域对象是关联域名   二、若是一个域名被攻击,那么另一个域名也有安全问题
cookie跨域 一、这种方法跨域的兼容性好,因为须要一些额外的设置,因此删除cookie的时候比较繁琐 必须保证两个域名为关联域名
postMessage跨域 一、这种方法能够直接实现将数据从A站点传输到B站点,并且解除了cookie的限制和JSONP没法获取要传入的站点的信息 这个为HTML5新增长的特性,浏览器的兼容性不是很好,低版本的浏览器不支持,具体能够见:CANIUSE网站

2.1 JSONP跨域

JSONP跨域主要的依据是利用一些HTML标签的“漏洞”,而后经过跨域的方式去调用这个在别的域名下面的脚本文件,JSONP跨域有script跨域

咱们先来一个简单的例子,咱们先下载一个phpStudy,而后配置两个本地服务器,分别为:www.test1.com、www.test2.com

在www.test1.com域名下面咱们添加一个test.js文件,内容以下:  

alert("test!");

 

而后在www.test2.com域名下面咱们添加一个HTML文件,以下所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script src="http://www.test1.com/test.js" />
</body>
</html>

这个相信大多数人都是见过的,可能你会说这个不就是简单的script脚本的引入吗?对的,就是由于script支持跨域,这个也就是咱们常见的CDN。可是上面的例子只能说是一个简单的JSONP跨域的应用而已,跨域的目的是要实现数据的传输。咱们能够按照这个思路这样去改写:

首先咱们能够在一个域名下面书写一个方法,而后经过在外部加载一个脚原本调用这个方法,向这个方法中去传值,这样就能够实现把外部的数据传到当前的域名的下面,从而实现了跨域。

咱们在test.js文件下面定义一个

test("tthis is js load script");

 www.test2.com下面的HTML文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        var test=function(data){
            alert(data);
        }
    </script>
    <script type="text/javascript" src="DEMO.js"></script>
</body>
</html>

这个咱们能够理解为是在一个HMTL文件中书写一个test方法,而后test方法容许调用的时候向里面传参,以后经过外部加载一个JS文件,JS文件实现的逻辑是调用这个方法,因此咱们不可以把定义的test方法放在调用的DEMO.js的文件后面,由于若是咱们这样作了的话,那么在调用外部的JS文件的时候,test方法没有加载进来因此咱们便看不到效果所在,这个有兴趣的同窗能够本身去试验一下,这里就不作试验了。

在实际的应用中,这样的例子是基本上不可能看到的,由于这样的参数传递太过于死板,应用性不强,咱们在实际的应用中应该要达到的效果是能够根据咱们的需求作出相应的响应,这样说吧,你们可能都知道或者调用过接口吧,接口其实咱们能够理解为就是JSONP的一种应用,接下来咱们就来试验一下:

说了这么多,你们应该仍是对具体怎样使用有点云里雾里的吧,如今咱们就基于JSONP的思想来制做一个简易的音乐专辑查询器

首先咱们应该要找到一个能够查询专辑的公共接口

http://cgi.music.soso.com/fcgi-bin/fcg_search_xmldata.q?source=10&w=关键字&perpage=1&ie=utf-8

而后咱们就编写以下的HTML代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>JSONP2</title>
</head>
<body>
    <input type="text" id="song" name="">
    <input type="button" id="song_search" value="歌曲搜索" name="">
    <br />
    <div style="width:200px;height:200px;background:pink" id="song_list"></div>
    <script type="text/javascript" src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
    <script type="text/javascript">
        var searchJsonCallback=function(data){
            //遍历查询结果
            var alb_html='';
            for(var i in data.list){
                alb_html+='<span>专辑:</span><div style="color:black">'+data.list[0].albumname+'</div>';
            }
            $("#song_list").html(alb_html);
        }
        $("#song_search").on("click",function(){
            var keyword=$("#song").val();
            if(keyword==undefined||keyword==""){
                alert("歌曲搜索不能为空");
                return false;
            }else{
                var url = "http://cgi.music.soso.com/fcgi-bin/fcg_search_xmldata.q?source=10&w="+keyword+"&perpage=1&ie=utf-8";
                // 建立script标签,设置其属性
                var script = document.createElement('script');
                script.setAttribute('src', url);
                // 把script标签加入head,此时调用开始
                document.getElementsByTagName('head')[0].appendChild(script); 
            }
            
        })    
    </script> 
</body>
</html>

 

效果以下:

 

这个就是JSOP在实际中的应用,若是直接用ajax来实现的话,原理也是同样,只不过要把script标签换成ajax调用而已。

若是你要所有使用jquery来实现的话,那么只须要把ajax中的dataType类型换成jsonp便可(经过上面的例子咱们知道script加载实际上也是一个get请求,这个能够在network中验证),这个有兴趣的同窗能够查一查资料。

 

2.2 iframe跨域

iframe跨域的原理跟script跨域同样,可是咱们要注意的是标签自身功能的差别性,具体差别以下:

一、script单纯就是引入的做用,可是iframe标签还有一个做用是显示的做用能够把远程加载的HTML代码显示出来,也就是script没法引入HTML代码文件

二、script标签只可以从远程获取数据,没法操做远程文件执行。可是iframe能够这样

上面的第二点提及来有点难理解,咱们就经过一个例子来讲明一下:

假设有这样的一个需求咱们须要在www.jsonp1.com下面调用一个方法来清除josnp2.com下面的本地存储,

首先咱们在jsonp1.com下面的index.html中加入script标签去调用jsonp2.com下面的js文件

具体以下:

window.localStorage.clear();
alert(1);

 

结果是:

咱们能够看到内容为1的弹窗,可是在jsonp2的本地存储就没有被清除,因此咱们能够得出结论,script标签的跨域适用于从远程获取数据,不适用对远程的操做。

这个需求咱们可使用iframe标签来实现:

jsonp1.com中的HTML文件以下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsonp1</title>
</head>
<body>
    <iframe src="http://www.jsonp2.com/demo.html"></iframe>
</body>
</html>

 

jsonp2下面的demo.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <script type="text/javascript">
        window.localStorage.clear();
        alert("清除成功");
    </script>
</body>
</html>

 

这样咱们就实现了经过在JSONP1中调用JSONP2中的文件来清除JSONP2中的本地存储

可是你们想想,咱们除了使用这种方法来控制远程操做以外,咱们还能够像script跨域同样来获取本地存储的数据吗?

答案是理论上是不能够实现的(本例子为博主的思路的构思,没有具体实践过,若有错误望各位指出),如图所示:

 首先咱们就在jsonp1.com网站下面的index.html文件下面经过iframe插入jsonp2.com/index.html,这样咱们就使用了iframe跨域,可是因为同源策略的限制,因此没法将jsonp2.com/index.html的值回传给jsonp1.com/index.html,因此这个时候在jsonp2.com/index.html是能够获取到站点的本地存储的,咱们就能够像上面音乐接口去使用,把本地储存中的数值,传递过去,可是这个时候jsonp1.com/index.html文件是没法直接获取接口中返回的东西的,也没法经过jsonp2.com/index.html回调,因此这种方法是不可行的

可是咱们就真的没法实现这样一个获取远程的站点的本地存储的功能吗?不是的这个时候利用window.name方法结合iframe来实现跨域

 

2.3 window.name 跨域

www.jsonp1.com下面的index.html代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>跨域获取数据</title>
    <script type="text/javascript">
    function domainData(url, fn)
    {
        var isFirst = true;
        var iframe = document.createElement('iframe');
        iframe.style.width=0;
        iframe.style.height=0;
        iframe.style.display = 'none';
        var loadfn = function(){
            if(isFirst){
                iframe.contentWindow.location = 'http://www.jsonp1.com/proxy.html';
                isFirst = false;
            } else {
                //alert(1);
                //console.log(iframe.contentWindow.name);
                alert(iframe.contentWindow.name);
                iframe.contentWindow.document.write('');
                iframe.contentWindow.close();
                document.body.removeChild(iframe);
                iframe.src = '';
                iframe = null;
            }
        };
        iframe.src = url;
        if(iframe.attachEvent){
            iframe.attachEvent('onload', loadfn);
        } else {
            iframe.onload = loadfn;
        }
         
        document.body.appendChild(iframe);
    }
    </script>
</head>
<body>
 
</body>
    <script type="text/javascript">
    domainData('http://www.jsonp2.com/demo.html', function(data){
        alert(data);
    });
    </script>
</html>

www.jsonp2.com/demo.html代码以下:

<script type="text/javascript">
        window.localStorage.setItem("test","123");
        var data=window.localStorage.getItem("test");
        window.localStorage.clear();
        window.name=data;
    </script>

这样咱们即可以实如今JSONP1的域名下面访问到JSONP2中的本地存储了,你们特别高兴有没有,反正这个需求当时作得时候也是挺困扰个人。

这里有一些注意事项要特别说明:

一、这个是在网上通过查找的代码,也能够说是一个比较标准的使用代码,你们在使用的时候能够参照业务需求进行该造。

二、在使用iframe这个标签以后要进行销毁,避免出现安全问题

三、window.name的使用必须创建在http协议的基础之上的,换句话来讲就是不能直接打开网页必定要配置相应的本地域名(直接打开本地网页采用的是file协议)

 

2.4 document.domain 跨域

 这个的实现思路跟cookie跨域类似,都是在两个关联域名中设置document.domain值,而后让这两个值相等,这样就能够实现跨域操做,具体实现不给出,自行百度

 

2.5 cookie跨域

cookie跨域这个没有什么好说的,不清楚的同窗请看我以前写过的一篇文章:cookie学习指南

 

2.6 postMessage跨域

 咱们仍是来实现一个上面的功能,在jsonp1域名下面获取jsonp2中localStorage的test字段的值,尝试着用postMessage来实现,具体的实现方式以下:

jsonp1.com下面的index.htm以下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div id="test"></div>
    <textarea id="textarea"></textarea>
    <iframe style="width:0px;height:0px" id="f" src="http://www.jsonp2.com/demo.html"></iframe>
    <script type="text/javascript" src="http://cdn.bootcss.com/jquery/3.1.1/jquery.js"></script>
    <script>
        var test1='';
        onmessage=function(e){
          e=e||event;
          // console.log(e);
          // console.log(e.data);
          test1=e.data;
          if(test1=="123"){
              alert("success!");
          }else{
              alert("error");
          }
         $("#test").html("<span style='color:red'>"+test1+"</span>");
        };
    </script>
</body>
</html>

 jsonp2.com中的demo.html内容:

<iframe id="f" src="http://www.jsonp1.com/index.html"></iframe>
<script>
var f=document.getElementById("f");
f.onload=function(){
    window.localStorage.setItem("test","123");
    var value=window.localStorage.getItem("test");
    window.localStorage.clear();
  f.contentWindow.postMessage(value,"http://www.jsonp1.com");
}
</script>

 这样就实现了一个从Jsonp2中获取本地存储的功能,可是在实践的过程当中存在的一些问题须要引发咱们的留意:

一、在两个须要跨域的文件都须要引入一个iframe来加载对方的路径

二、咱们在使用的时候,是使用postMessage来发送信息给对方,而后咱们是经过监控message事件来获取信息的

 

3、总结

这个篇文章主要总结了一些关于前端跨域方面的工做实践,与一些问题的探索,同时若是发现错误的话, 也但愿各位可以指正。

相关文章
相关标签/搜索