老生常谈的跨域处理


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 相关文章推荐
Javascript里使用Dom操作Xml
Jan 22 Javascript
js 实现无干扰阴影效果 简单好用(附文件下载)
Dec 27 Javascript
css transform 3D幻灯片特效实现步骤解读
Mar 27 Javascript
JavaScript加强之自定义event事件
Sep 21 Javascript
Angular用来控制元素的展示与否的原生指令介绍
Jan 07 Javascript
详解AngularJS Filter(过滤器)用法
Dec 28 Javascript
原生JS实现图片懒加载(lazyload)实例
Jun 13 Javascript
js基于FileSaver.js 浏览器导出Excel文件的示例
Aug 15 Javascript
微信小程序基于本地缓存实现点赞功能的方法
Dec 18 Javascript
Vue组件全局注册实现警告框的实例详解
Jun 11 Javascript
vue axios重复点击取消上一次请求封装的方法
Jun 19 Javascript
JavaScript逻辑运算符相关总结
Sep 04 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中几种常见安全设置详解
2010/04/06 PHP
PHP中文分词的简单实现代码分享
2011/07/17 PHP
浏览器关闭后,能继续执行的php函数(ignore_user_abort)
2012/08/01 PHP
通过dbi使用perl连接mysql数据库的方法
2014/04/16 PHP
PHP开源开发框架ZendFramework使用中常见问题说明及解决方案
2014/06/12 PHP
PHP中基本HTTP认证技巧分析
2015/03/16 PHP
PHP生成随机数的方法总结
2018/03/01 PHP
开发跨浏览器javascript常见注意事项
2009/01/01 Javascript
jQuery结合Json提交数据到Webservice,并接收从Webservice返回的Json数据
2011/02/18 Javascript
js移除事件 js绑定事件实例应用
2012/11/28 Javascript
JS清除选择内容的方法
2015/01/29 Javascript
jQuery中slideUp 和 slideDown 的点击事件
2015/02/26 Javascript
JavaScript显示表单内元素数量的方法
2015/04/02 Javascript
javascript获取select值的方法分析
2015/07/02 Javascript
javascript截图 jQuery插件imgAreaSelect使用详解
2016/05/04 Javascript
JavaScript的new date等日期函数在safari中遇到的坑
2016/10/24 Javascript
vue编译打包本地查看index文件的方法
2018/02/23 Javascript
Vue中v-for的数据分组实例
2018/03/07 Javascript
Vue实现push数组并删除的例子
2019/11/01 Javascript
JavaScript前端开发时数值运算的小技巧
2020/07/28 Javascript
[08:40]Navi Vs Newbee
2018/06/07 DOTA
用Python将mysql数据导出成json的方法
2018/08/21 Python
用Python配平化学方程式的方法
2019/07/20 Python
python 初始化一个定长的数组实例
2019/12/02 Python
如何给Python代码进行加密
2020/01/10 Python
香港永安旅游网:Wing On Travel
2017/04/10 全球购物
印度化妆品购物网站:Nykaa
2018/07/22 全球购物
Aeropostale官网:美国著名校园品牌及青少年服饰品牌
2019/03/21 全球购物
100%法国制造的游戏和玩具:Les Jouets Français
2021/03/02 全球购物
趣味游戏活动方案
2014/02/07 职场文书
学校施工安全责任书
2015/01/29 职场文书
幼师辞职信范文
2015/02/27 职场文书
2015年幼儿园班主任工作总结
2015/05/12 职场文书
闭幕词的写作格式与范文!
2019/06/24 职场文书
eclipse创建项目没有dynamic web的解决方法
2021/06/24 Java/Android
Win11应用商店打开闪退怎么解决? win11应用商店打不开的多种解决办法
2022/04/05 数码科技