老生常谈的跨域处理

阅读目录

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

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

什么是跨域

在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.jshttp://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值保持不变的原理。兼容性好。需要照顾落后的浏览器时,首选。

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

(0)

相关推荐

  • Nginx服务器中处理AJAX跨域请求的配置方法讲解

    Nginx 实现AJAX跨域请求 AJAX从一个域请求另一个域会有跨域的问题.那么如何在nginx上实现ajax跨域请求呢?要在nginx上启用跨域请求,需要添加add_header Access-Control*指令.如下所示: location /{ add_header 'Access-Control-Allow-Origin' 'http://other.subdomain.com'; add_header 'Access-Control-Allow-Credentials' 'true

  • 使用C#处理WebBrowser控件在不同域名中的跨域问题

    我们在做web测试时,经常会使用WebBrowser来进行一些自动化的任务.而有些网页上面会用IFrame去嵌套别的页面,这些页面可能不是在相同域名下的,这时就会出现跨域问题,无法直接在WebBrowser中获取到IFrame中的元素.下面来做个试验,自己写个页面嵌套一个百度的首页,然后在我们自己的页面上输入要查询的词,最后在百度上自动完成搜索. 复制代码 代码如下: <!DOCTYPE html> <html lang="en" xmlns="http:/

  • 5种处理js跨域问题方法汇总

    前两天碰到一个跨域问题的处理,使用jsonp可以解决.(http://www.jb51.net/article/57889.htm) 最近再整理了一下: 1.jsonp. ajax请求,dataType为jsonp.这种形式需要请求在服务端调整为返回callback([json-object])的形式.如果服务端返回的是普通json对象.那么调试的时候,在chrome浏览器的控制台会报"Uncaught SyntaxError: Unexpected token"错误:在firefox

  • jquery中ajax处理跨域的三大方式

    由于JS同源策略的影响,因此js只能访问同域名下的文档.因此要实现跨域,一般有以下几个方法: 一.处理跨域的方式: 1.代理 2.XHR2 HTML5中提供的XMLHTTPREQUEST Level2(及XHR2)已经实现了跨域访问.但ie10以下不支持 只需要在服务端填上响应头: header("Access-Control-Allow-Origin:*"); /*星号表示所有的域都可以接受,*/ header("Access-Control-Allow-Methods:G

  • 老生常谈的跨域处理

    阅读目录 什么是跨域 常用的几种跨域处理方法: 跨域的原理解析及实现方法 总结 摘要:跨域问题,无论是面试还是平时的工作中,都会遇到,本文总结处理跨域问题的几种方法以及其原理,也让自己搞懂这方面的知识,走起. 什么是跨域 在JavaScript中,有一个很重要的安全性限制,被称为"Same-Origin Policy"(同源策略).这一策略对于JavaScript代码能够访问的页面内容做了很重要的限制,即JavaScript只能访问与包含它的文档在同一域下的内容. JavaScript

  • JQuery用$.ajax或$.getJSON跨域获取JSON数据的实现代码

    通过JQuery可以跨域获取JSON数据,但必须弄清楚的是,JQuery不可以跨域获取任意JSON格式的数据,必须要通过服务端输出特定的针对JQuery跨域读取的JSON数据.你可能目前对此仍然毫无了解,没关系,本文将以最简单易懂的方式介绍这个技术,相信人人都容易读懂,并能够实际应用. JQuery获取同域的JSON数据 首先引用jQuery库文件: <script src="http://apps.bdimg.com/libs/jquery/1.9.0/jquery.min.js&quo

  • JSONP跨域GET请求解决Ajax跨域访问问题

    前几天,工作上有一新需求,需要前端web页面异步调用后台的Webservice方法返回信息.实现方法有多种,本例采用jQuery+Ajax,完成后,在本地调试了一切ok,但是部署到服务器上以后就出现问题了,后台服务调用没有响应,怎么回事?代码没怎么改动,唯一修改的地方就是jQuery的ajax方法中的url地址.难道是这里的问题,经过检查和调试,发现原来是同源策略在作怪,我们知道,JavaScript或jQuery是在Web前端开发中经常使用的动态脚本技术.在JavaScript中,有一个很重要

  • extjs_02_grid显示本地数据、显示跨域数据

    1.显示表格 http://img.blog.csdn.net/20140622133941015?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWRhbV93enM=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast <%@ page language="java" import="java.util.*" pag

  • 解析AngularJS中get请求URL出现的跨域问题

    今天早上帮助同学看了一个AngularJS的问题,主要是请求中出现了跨域访问,请求被阻止. 下面是她给我的代码: <html lang="en" ng-app="myApp"> <head> <meta charset="UTF-8"> <title>Title</title> <!--<script src="../js/jquery-1.11.0.js"

  • 详解AngularJS如何实现跨域请求

    跨域,前端开发中经常遇到的问题,AngularJS实现跨域方式类似于Ajax,使用CORS机制. 下面阐述一下AngularJS中使用$http实现跨域请求数据. AngularJS XMLHttpRequest:$http用于读取远程服务器的数据 $http.post(url, data, [config]).success(function(){ ... }); $http.get(url, [config]).success(function(){ ... }); $http.get(ur

  • 浅谈angular.js跨域post解决方案

    跨域,前端开发中经常遇到的问题,AngularJS实现跨域方式类似于Ajax,使用CORS机制. 下面阐述一下AngularJS中使用$http实现跨域请求数据. AngularJS XMLHttpRequest:$http用于读取远程服务器的数据 $http.post(url, data, [config]).success(function(){ ... }); $http.get(url, [config]).success(function(){ ... }); $http.get(ur

  • Angular实现跨域(搜索框的下拉列表)

    angular.js 自带jsonp,实现跨域,下面来实现搜索框的下拉列表,使用百度和360分别尝试一下 百度:url截取之后红色部分需替换 :https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=数据&cb=JSON_CALLBACK 360:https://sug.so.360.cn/suggest?callback=JSON_CALLBACK&word=数据 注意:需要在服务器环境下运行 思路: 1 .声明 angular 2

  • Springmvc ajax跨域请求处理方法实例详解

    上次给一个网站写网站  前后端分离 最后跪在ajax跨域上面了  自己在网上找了个方法  亲试可用  记录一下 写一个类  继承HandlerInterceptorAdapter package com.util; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.handler.Ha

  • 原生js的ajax和解决跨域的jsonp(实例讲解)

    最近慢慢感觉,学再多框架,库,都不如老老实实先把基础弄扎实了. 不说废话,先上一个用ajax请求下本地的一个.txt文件 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script> window.onload =function(){ var oBtn = d

随机推荐