老生常谈的跨域处理


Posted in Javascript onJanuary 11, 2017

阅读目录

  • 什么是跨域
  • 常用的几种跨域处理方法:
  • 跨域的原理解析及实现方法
  • 总结

摘要:跨域问题,无论是面试还是平时的工作中,都会遇到,本文总结处理跨域问题的几种方法以及其原理,也让自己搞懂这方面的知识,走起。

什么是跨域

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

JavaScript这个安全策略在进行多iframe或多窗口编程、以及Ajax编程时显得尤为重要。根据这个策略,在baidu.com下的页面中包含的JavaScript代码,不能访问在google.com域名下的页面内容;甚至不同的子域名之间的页面也不能通过JavaScript代码互相访问。对于Ajax的影响在于,通过XMLHttpRequest实现的Ajax请求,不能向不同的域提交请求,例如,在abc.example.com下的页面,不能向def.example.com提交Ajax请求,等等。

为什么浏览器要实现同源限制?我们举例说明:

比如一个黑客,他利用iframe把真正的银行登录页面嵌到他的页面上,当你使用真实的用户名和密码登录时,如果没有同源限制,他的页面就可以通过javascript读取到你的表单中输入的内容,这样用户名和密码就轻松到手了.

又比如你登录了OSC,同时浏览了恶意网站,如果没有同源限制,该恶意 网站就可以构造AJAX请求频繁在OSC发广告帖.

跨域的情况分为以下几种:

老生常谈的跨域处理

特别注意两点:

 1、如果是协议和端口造成的跨域问题“前台”是无能为力的

 2、在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。比如上面的,http://www.a.com/a.js和http://70.32.92.74/b.js。虽然域名和域名的ip对应,不过还是被认为是跨域。

“URL的首部”指window.location.protocol +window.location.host。其中,

window.location.protocol:指含有URL第一部分的字符串,如http:

window.location.host:指包含有URL中主机名:端口号部分的字符串.如//www.cenpok.net/server/

常用的几种跨域处理方法:

 1、JSONP

 2、CORS策略

 3、document.domain+iframe的设置

 4、HTML5的postMessage

 5、使用window.name来进行跨域

跨域的原理解析及实现方法

1、JSONP(JSON with padding)

原理 :

我们知道,在页面上有三种资源是可以与页面本身不同源的。它们是:js脚本,css样式文件,图片,像淘宝等大型网站,肯定会将这些静态资源放入cdn中,然后在页面上连接,如下所示,所以它们是可以链接访问到不同源的资源的。

1)<script type="text/javascript" src="某某cdn地址" ></script>

2)<link type="text/css" rel="stylesheet" href="某个cdn地址" />

3)<img src="某个cdn地址" alt=""/>

而jsonp就是利用了script标签的src属性是没有跨域的限制的,从而达到跨域访问的目的。因此它的最基本原理就是:动态添加一个<script>标签来实现。

实现方法:

这里是使用ajax来请求的,看起来和ajax没啥区别,其实还是有区别的。

ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加<script>标签来调用服务器提供的js脚本。

$.ajax({ 
    url:"http://crossdomain.com/services.php", 
    dataType:'jsonp', 
    data:'', 
    jsonp:'callback', 
    success:function(result) { 
      // some code
    } 
  });

上面的代码中,callback是必须的,callback是什么值要跟后台拿。获取到的jsonp数据格式如下:

flightHandler({
  "code": "CA1998",
  "price": 1780,
  "tickets": 5
});

jsonp的全称为json with padding,上面的数据中,flightHandler就是那个padding.

JSONP的不足之处:

1、只能使用get方法,不能使用post方法:

我们知道 script,link, img 等等标签引入外部资源,都是 get 请求的,那么就决定了 jsonp 一定是 get 的。但有时候我们使用的 post 请求也成功,为啥呢?这是因为当我们指定dataType:'jsonp',不论你指定:type:"post" 或者type:"get",其实质上进行的都是 get 请求!

2、没有关于 JSONP 调用的错误处理。如果动态脚本插入有效,就执行调用;如果无效,就静默失败。失败是没有任何提示的。例如,不能从服务器捕捉到 404 错误,也不能取消或重新开始请求。不过,等待一段时间还没有响应的话,就不用理它了。

2、CORS策略

原理:

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。它为Web服务器定义了一种方式,允许网页从不同的域访问其资源.

CORS系统定义了一种浏览器和服务器交互的方式来确定是否允许跨域请求。 它是一个妥协,有更大的灵活性,但比起简单地允许所有这些的要求来说更加安全。

实现方法:

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。

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

前端方面

以前我们使用Ajax,代码类似于如下的方式:

var xhr = new XMLHttpRequest(); 
xhr.open("GET", "/hfahe", true); 
xhr.send(); 
// 这里的“/hfahe”是本域的相对路径。

如果我们要使用CORS,相关Ajax代码可能如下所示:

var xhr = new XMLHttpRequest(); 
xhr.open("GET", "http://blog.csdn.net/hfahe", true); 
xhr.send(); 
// 请注意,代码与之前的区别就在于相对路径换成了其他域的绝对路径,也就是你要跨域访问的接口地址。

服务器方面

服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。

CORS策略的优缺点:

优点:

 1、CORS支持所有类型的HTTP请求。

 2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。

缺点: 兼容性方面相对差一点,ie10或以上才支持

3、document.domain+iframe的设置  (只有在主域相同的时候才能使用该方法)

原理:

浏览器中不同域的框架之间是不能进行js的交互操作的。但是不同的框架之间(父子或同辈),是能够获取到彼此的window对象的,但是,我们也只能获取到一个几乎

无用的window对象。比如,有一个页面,它的地址是 http://www.example.com/a.html , 在这个页面里面有一个iframe,它的src是 http://example.com/b.html , 很显然,这

个页面与它里面的iframe框架是不同域的,所以我们是无法通过在页面中书写js代码来获取iframe中的东西的。

这个时候,document.domain就可以派上用场了,我们只要把 http://www.example.com/a.html 和  http://example.com/b.html 这两个页面的document.domain都设成

相同的域名就可以了。但要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。例如:

a.b.example.com 中某个文档的document.domain 可以设成a.b.example.com、b.example.com 、example.com中的任意一个,但是不可以设成 c.a.b.example.com,因为这是

当前域的子域,也不可以设成baidu.com,因为主域已经不相同了。

使用方法:

比如在http://www.example.com/a.html 的页面里要访问 http://example.com/b.html里面的东西。

在页面 http://www.example.com/a.html 中设置document.domain:

//http://www.example.com/a.html
<html>
<head>
  <title>A页面</title>
  <script type="text/javascript" src="jquery.js"></script>
</head>
<body>
  <div>A页面</div>
  <iframe id="iframe" src="http://example.com/b.html" style="display:none;"></iframe>
// 相当于用一个隐藏的iframe来做代理
  <script>
    $(function(){
      try{
        document.domain = "example.com"; //这里将document.domain设置成一样
      }catch(e){}
      $("#iframe").load(function(){
        var iframe = $("#iframe").contentDocument.$;
        ifram.get("http://example.com/接口",function(data){});
      });
    });
  </script>
<body>
</html>

在页面 http://example.com/b.html 中也设置document.domain,而且这也是必须的,虽然这个文档的domain就是example.com,但是还是必须显示的设置document.domain的值:

//http://example.com/b.html
<html>
<head>
  <title>B页面</title>
  <script type="text/javascript" src="jquery.js"></script>
</head>
<body>
  <div>B页面</div>
  <script>    
    $(function(){
      try{
      document.domain = "example.com"; //这里将document.domain设置成一样
      }catch(e){}
    });
  </script>
</body>
</html>

这里有个注意点,就是在A页面中,要等iframe标签完成加载B页面之后,再取iframe对象的contentDocument,否则如果B页面没有被iframe完全加载,在A页面中通过contentDocument属性就取不到B页面中的jQuery对象。

一旦取到B页面中的jQuery对象,就可以直接发ajax请求了,这种类似“代理”方式可以解决主子域的跨域问题。

缺点:

只有在主域相同的时候才能使用该方法

4、HTML5的postMessage

原理:

没啥原理,就是一个html5所提供的一个API.--->HTML5 window.postMessage是一个安全的、基于事件的消息API。

在需要发送消息的源窗口调用postMessage方法即可发送消息。其中.源窗口可以是全局的window对象,也可以是以下类型的窗口:

1、文档窗口中的iframe:

var iframe = document.getElementById('my-iframe');
var win = iframe.documentWindow;

2、JavaScript打开的弹窗:

var win = window.open();

3、当前文档窗口的父窗口:

var win = window.parent;

4、

var win = window.opener();

发送消息:找到源window对象后,即可调用postMessage API向目标窗口发送消息:

win.postMessage(msg, targetOrigin);

说明:postMessage函数接收两个参数:

    1、msg, 将要发送的消息,可以使一切javascript参数,如字符串,数字,对象,数组等。

    2、targetOrigin,这个参数称作“目标域”,注意,是目标域不是本域!比如,你想在2.com的网页上往1.com网页上传消息,那么这个参数就是“http://1.com/”,而不是2.com.协议,(一个完整的域名包括:主机名,端口号。如:http://g.cn:80/)

接收消息:那目标窗口要怎么接收传过来的数据呢,只要监听window的message事件就可以接收了。

var onmessage = function (event) {
  var data = event.data;
  var origin = event.origin;
  //do someing
};
if (typeof window.addEventListener != 'undefined') {
  window.addEventListener('message', onmessage, false);
} else if (typeof window.attachEvent != 'undefined') {
  //for ie
  window.attachEvent('onmessage', onmessage);
}

message事件监听函数接收一个参数,Event对象实例,该对象有三个属性:

  • data : 消息
  • origin:消息的来源地址
  • source:发送消息窗口的window对象引用

使用方法(案例):

http://test.com/index.html--> 发送消息的页面

<!-- 这个是 http://test.com/index.html 页面 -->
<div>
  <!-- 要给下面的页面传一个妹子过去 -->
  <iframe id="child" src="http://lsLib.com/lsLib.html"></iframe> 
</div>
<script type="text/javascript">
  window.onload=function(){
    window.frames[0].postMessage('苍老师','http://lslib.com');
  }
</script>

http://lslib.com/lslib.html --> 接收消息的页面

<!-- 这个是 http://lslib.com/lslib.html 页面 -->
<script type="text/javascript">
  window.addEventListener('message',function (e) {
    console.log(e.origin,e.data);
    alert('收到妹子一枚:'+e.data);
  });
</script>

优缺点:

优点:方便,安全,有效的解决了跨域问题

缺点:万恶的资本主义,ie8+才支持,而且ie8+<ie10只支持iframe的方式。

5、使用window.name来进行跨域(相对比较完美的方法)

原理:

当iframe的页面跳到其他地址时,其window.name值保持不变,并且可以支持非常长的 name 值(2MB)。

浏览器跨域iframe禁止互相调用/传值.但是调用iframe时 window.name 却不变,正是利用这个特性来互相传值,当然跨域下是不容许读取ifram的window.name值.

所以这里我们还要准备一个和主页面http://www.a.com/main.html 相同域下的代理页面http://www.a.com/other.html ,iframe调用子页面 http://www.b.com/data.html

使用方法:

1、准备三个页面:

 http://www.a.com/main.html   //应用页面

 http://www.a.com/other.html    // 代理页面,要求和应用页面在同一个域。一般是一个空的html

    http://www.b.com/data.html   //应用页面获取数据的页面,简称:数据页面

2、数据页面将数据传到window.name中去。如下:

http://www.b.com/data.html中的 data.html

// data.html
window.name="苍老师"; //可以是其他类型的数据,比如数组,对象等等

http://www.a.com/main.html   //应用页面的代码如下:

<!-- main.html -->
var iframeData;
var state = 0;//开关变量
var iframe = document.createElement('iframe'); //创建iframe
var loadfn = function() {
  if (state === 1) {
    iframeData = iframe.contentWindow.name; // 读取数据
    alert('获取到了iframe传过来的妹子'+iframeData);
  }else if (state === 0) {
     state = 1;
     iframe.contentWindow.location = 'http://www.a.com/other.html'; //这里是代理页面 other.html
     /**
       这里说明一下:
       由于iframe的location改变了,相当于重新载入页面(这是iframe的性质决定的),于是重新执行loadfn方法。


  由于当iframe的页面跳到其他地址时,其window.name值保持不变,并且此时开关变量 state已经变为1,
       于是就可以获取到window.name值,也就达到了跨域访问的目的了。
    **/
  };
}
iframe.src = 'http://www.b.com/data.html'; //这是是数据页面,data.html
if (iframe.attachEvent) {
  iframe.attachEvent('onload', loadfn);
} else {
  iframe.onload = loadfn;
}
document.body.appendChild(iframe);

3、获取数据以后销毁这个iframe,释放内存;这也保证了安全(不被其他域frame js访问)。

iframe.contentWindow.document.write('');
 iframe.contentWindow.close();
 document.body.removeChild(iframe);

优缺点:

浏览器支持情况好,是比较普遍的使用方法

总结

以上总结了js跨域的几种方法,当然还有其他的方法,不过没有。他们各有千秋。其实最主要的区别除了实现方式不一样,主要是浏览器的兼容问题而已。

JSONP:

JSONP的优点是:它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持;并且在请求完毕后可以通过调用callback的方式回传结果。

JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求;它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

CORS策略

优点:使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。

缺点:古老的浏览器不支持,不过大部分现代浏览器都支持

document.domain+iframe:只适用于主域相同的跨域问题处理

html5的postMessage:

优点:是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。如果是现代浏览器,首选。

缺点: ie8以前不支持

window.name:

主要是应用当frame的页面跳到其他地址时,其window.name值保持不变的原理。兼容性好。需要照顾落后的浏览器时,首选。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
发一个自己用JS写的实用看图工具实现代码
Jul 26 Javascript
Wordpress ThickBox 添加“查看原图”效果代码
Dec 11 Javascript
解析prototype,JQuery中跳出each循环的方法
Dec 12 Javascript
Javascript基础教程之JavaScript语法
Jan 18 Javascript
JavaScript拖拽、碰撞、重力及弹性运动实例分析
Jan 08 Javascript
javascript每日必学之继承
Feb 23 Javascript
JS实现的颜色实时渐变效果完整实例
Mar 25 Javascript
实例讲解Jquery中隐藏hide、显示show、切换toggle的用法
May 13 Javascript
Vue2.0组件间数据传递示例
Mar 07 Javascript
微信小程序引用公共js里的方法的实例详解
Aug 17 Javascript
JavaScript登录验证基础教程
Nov 01 Javascript
在Vue项目中,防止页面被缩放和放大示例
Oct 28 Javascript
bootstrap选项卡使用方法解析
Jan 11 #Javascript
常用的javascript设计模式
Jan 11 #Javascript
WebView启动支付宝客户端支付失败的问题小结
Jan 11 #Javascript
Bootstrap实现提示框和弹出框效果
Jan 11 #Javascript
分享bootstrap学习笔记心得(组件及其属性)
Jan 11 #Javascript
jQuery实现CheckBox全选、全不选功能
Jan 11 #Javascript
理解javascript中的闭包
Jan 11 #Javascript
You might like
第六章 php目录与文件操作
2011/12/30 PHP
PHP入门之常量简介和系统常量
2014/05/12 PHP
Linux系统下使用XHProf和XHGui分析PHP运行性能
2015/12/08 PHP
javascrip客户端验证文件大小及文件类型并重置上传
2011/01/12 Javascript
JS重要知识点小结
2011/11/06 Javascript
深入理解javascript动态插入技术
2013/11/12 Javascript
简介AngularJS的视图功能应用
2015/06/17 Javascript
jQuery实现下拉框选择图片功能实例
2015/08/08 Javascript
Javascript基础知识盲点总结之函数
2016/05/15 Javascript
Nodejs 搭建简单的Web服务器详解及实例
2016/11/30 NodeJs
简单三步实现报表页面集成天气
2016/12/15 Javascript
vue.js选中动态绑定的radio的指定项
2017/06/02 Javascript
Angularjs中ng-repeat的简单实例
2017/08/25 Javascript
SeaJS中use函数用法实例分析
2017/10/10 Javascript
vue代理和跨域问题的解决
2018/07/18 Javascript
详解Vue+axios+Node+express实现文件上传(用户头像上传)
2018/08/10 Javascript
Vue实现根据hash高亮选项卡
2019/05/27 Javascript
[07:47]DOTA2国际邀请赛采访专栏:探访Valve总部
2013/08/08 DOTA
Python判断列表是否已排序的各种方法及其性能分析
2016/06/20 Python
Python利用turtle库绘制彩虹代码示例
2017/12/20 Python
python+selenium实现163邮箱自动登陆的方法
2017/12/31 Python
使用python脚本实现查询火车票工具
2018/07/19 Python
python求解数组中两个字符串的最小距离
2018/09/27 Python
搞定这套Python爬虫面试题(面试会so easy)
2019/04/03 Python
python中struct模块之字节型数据的处理方法
2019/08/27 Python
.img/.hdr格式转.nii格式的操作
2020/07/01 Python
HTML5探秘:用requestAnimationFrame优化Web动画
2018/06/03 HTML / CSS
GUESS德国官网:美国牛仔服装品牌
2017/02/14 全球购物
ktv收银员岗位职责
2013/12/16 职场文书
自我鉴定书面格式
2014/01/13 职场文书
2014年寒假社会实践活动心得体会
2014/04/07 职场文书
校庆口号
2014/06/20 职场文书
2015年父亲节寄语
2015/03/23 职场文书
电力工程合作意向书
2015/05/11 职场文书
运动会广播稿100字
2015/08/19 职场文书
解决 Redis 秒杀超卖场景的高并发
2022/04/12 Redis