前端跨域的那些事

这一节,我们来讲一讲,前端跨域的那些事,主要分成这样的几部分来讲解,

一、为什么要跨域?

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

  2.1 JSONP跨域

  2.2 iframe跨域

  2.3 window.name 跨域

  2.4 document.domain 跨域

  2.5 cookie跨域

  2.6 postMessage跨域

三、总结

一、为什么要跨域

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

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

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

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

跨域方式 特点 局限
JSONP跨域 1、JSONP跨域不是一种前端技术,而是程序猿创造的一种跨域方法
2、JSONP跨域,是一种简单的跨域方法,兼容性比较好
 
iframe 跨域 1、操作简便 2、兼容性好 单纯的使用iframe跨域无法获取其他域名下的数据
window.name 跨域 1、必须与iframe配合使用 2、可以获取其他域名下的数据  
document.domain 跨域 1、必须保证两个要跨域的对象是有一个关联域名   1、只针对两个跨域对象是关联域名   2、如果一个域名被攻击,那么另外一个域名也有安全问题
cookie跨域 1、这种方法跨域的兼容性好,由于需要一些额外的设置,所以删除cookie的时候比较繁琐 必须保证两个域名为关联域名
postMessage跨域 1、这种方法可以直接实现将数据从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跨域一样,但是我们要注意的是标签自身功能的差异性,具体差异如下:

1、script单纯就是引入的作用,但是iframe标签还有一个作用是显示的作用可以把远程加载的HTML代码显示出来,也就是script无法引入HTML代码文件

2、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中的本地存储了,大家特别高兴有没有,反正这个需求当时做得时候也是挺困扰我的。

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

1、这个是在网上经过查找的代码,也可以说是一个比较标准的使用代码,大家在使用的时候可以参照业务需求进行该造。

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

3、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中获取本地存储的功能,但是在实践的过程中存在的一些问题需要引起我们的留意:

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

2、我们在使用的时候,是使用postMessage来发送信息给对方,然后我们是通过监控message事件来获取信息的

三、总结

这个篇文章主要总结了一些关于前端跨域方面的工作实践,与一些问题的探索,同时如果发现错误的话, 也希望各位能够指正。

时间: 2024-10-03 07:17:43

前端跨域的那些事的相关文章

前端跨域问题的几种解决方案

前端跨域问题 一:同源策略 1.what's this 所谓同源是指,域名,协议,端口相同.当浏览器运行一个JS脚本时会进行同源检测,如果不同源是不能执行的. 2.源继承 来自about:blank,javascript:和data:URLs中的内容,继承了将其载入的文档所指定的源,因为它们的URL本身未指定任何关于自身源的信息. 3.变更源 变更源可以实现基础域相同的不同页面的跨域问题. 如:a.baidu.com/index.html 通过 iframe 引入 b.baidu.com/ind

解决前端跨域请求的几种方式

利用 JSONP 实现跨域调用 说道跨域调用,可能大家首先想到的或者听说过的就是 JSONP 了. 1.1 什么是JSONP JSONP 是 JSON 的一种使用模式,可以解决主流浏览器的跨域数据访问问题.其原理是根据 XmlHttpRequest 对象受到同源策略的影响,而 <script> 标签元素却不受同源策略影响,可以加载跨域服务器上的脚本,网页可以从其他来源动态产生 JSON 资料.用 JSONP 获取的不是 JSON 数据,而是可以直接运行的 JavaScript 语句. 1.2

前端跨域访问

1. JSONP 2. CORS(Cross-origin resource sharing) 2.1 运行模式 2.2 JQuery支持CORS 2.3 与JSONP相比 3. 跨域访问在点评的应用 References 在互联网应用中: 一个页面需要请求多个域名下的web服务端接口 同时一个web服务接口可能会被很多不同域名下的页面请求. 一个web应用如果支持为了支持以上模式而申请多个域名是不合算的,因为域名申请和管理所占用的资源比较大,因此服务端支持跨域就成了一个更合理的解决方案.解决跨

前端跨域几种方式

跨域问题的直接原因是浏览器存在同源策略,浏览器同源指的是:两个页面的协议.端口和主机相同,则两个页面具有相同的源.IE下满足协议.主机相同,就认为是同源. 想象一下,如果没有同源策略,谁都可以修改你站点上的内容,读取你的cookie,后果难以想象 前端跨域的几种方式 修改document.domain document.domain 用来获取当前网页的域名,document.domain可以被赋值 document.domain只能修改成当前域名的主域名或者基础域名,如当前域名是b.360.cn

前端跨域请求原理及实践

前端跨域请求原理及实践 2017-03-03 前端大全 (点击上方公众号,可快速关注) 作者:高鹏 tingandpeng.com/2016/09/05/前端跨域请求原理及实践/ 如有好文章投稿,请点击 → 这里了解详情 一. 跨域请求的含义 浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不同域的服务进行跨站调用. 一般的,只要网站的 协议名protocol. 主机host. 端口号port 这三个中的任意一个不同,网站间的数据请求与传输便构成了跨域调用.

前端跨域杂谈

1.前端跨域之表单(post) 项目需求,需要跨域向另一台服务器传送大量值,从ajax角度跨域是get方式,基于此,方案采用构造<form>表单,通过action发送到对方服务器,对方服务器需要做一些配合. 发送方post.html <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta content="width=device-width,

.net mvc webapi 解决前端跨域问题

跨域问题的原因不解释了,直接设置两步就可以解决前端跨域问题 1.Gloabel.asax文件中 //解决跨域问题 protected void Application_BeginRequest(object sender, EventArgs e) { RegisterRoutes(RouteTable.Routes); if (HttpContext.Current.Request.HttpMethod == "OPTIONS") { HttpContext.Current.Resp

前端跨域方案-跨域请求代理(asp.net handler)

现在技术开发偏向于使用统一的接口处理浏览器或者app的http请求. 大家都知道因为浏览器的同源策略的原因 js直接请求webapi 接口会有一些问题,即使做好服务器端的配置 同样会有不少的 问题  并且会有浏览器的兼容性 而使用jsonp 又需要服务器端对返回数据做相关处理 所以考虑考虑使用代理来解决前端跨域请求的问题. 代理程序走asp.net的一般处理程序,来实现前端js请求的接受然后转发到api站点. 关键点: 1.使用url参数的方式传送api接口的站点路径 http://test.m

html5的postmessage实现js前端跨域访问及调用解决方案

关于跨域访问,使用JSONP的方法,我前面已经demo过了,具体见http://supercharles888.blog.51cto.com/609344/856886,HTML5提供了一个非常强大的API,叫postMessage,它其实就是以前iframe的进化版本,使用起来极其方便,这里举个实验例子: 我们依旧按照与上文相同的设定,假定我们有2个Domain Domain1: http://localhost:8080  它上面有个应用叫HTMLDomain1,并且有个页面叫sender.