Fork me on GitHub

实现跨域请求的方法整理

  • 这里说的JS跨域是指通过JS在不同的域之间进行数据传输或通信,比如:
    • 用ajax向一个不同的域请求数据
    • 通过JS获取页面中不同域的框架中(iframe)的数据
  • 只要协议域名端口有任何一个不同,都被当作是不同的域。

浏览器的同源策略

  • 只有当协议端口域名都相同的页面,则两个页面具有相同的源。
  • 同源策略是指一段脚本只能读取来自同一来源的窗口和文档的属性,这里的同一来源指的是主机名、协议和端口号的组合。只要这三个中的任意一个不同,网站间的数据请求与传输便构成了跨域调用,则受到同源策略的限制。
  • 同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互*。这是一个用于隔离潜在恶意文件的关键的安全机制。浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不同域的服务进行跨站调用(通常指使用XMLHttpRequest请求)。
  • 默认情况下,XHR对象只能访问与包含它的页面位于同一个域中的资源请求源请求对象必须在一个域内。

跨域请求方式

  • 解决跨域问题,最简单的莫过于通过nginx反向代理进行实现,但是其需要在运维层面修改,且有可能请求的资源并不在我们控制范围内(第三方),所以该方式不能作为通用的解决方案,下面阐述了经常用到几种跨域方式:
图片ping或script标签跨域
  • <script><img><link>标签不会遇到跨域问题。严格意义上讲,这不是跨域,这只是跨站资源请求
  • 跨域是指在脚本代码中非同源域发送HTTP请求
  • 图片ping常用于跟踪用户点击页面或动态广告曝光次数。

    • 使用<img>标签,因为网页可以从任何网页中加载图片,而不用担心跨域。
    • 请求数据通过字符串形式发送,而响应可以是任何内容。
    • 这种方法,1)只能发送get请求。2)浏览器无法获取服务器的响应数据。3)只适用于浏览器与服务器之间的单向通信
    • 图片ping的示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      <input id="btn" type="button" value="跨域请求">
      <div id="result"></div>
      <script>
      var add = (function(){
      var counter = 0;
      return function(){
      return ++counter;
      }
      })();
      btn.onclick = function(){
      var sum = add();
      var img = result.getElementsByTagName('img')[0];
      if(!img){
      var img = new Image();
      }
      img.height="100";
      img.onload = img.onerror = function(){
      result.appendChild(img);
      var oSpan = document.getElementById('sum');
      if(!oSpan){
      oSpan = document.createElement('span');
      oSpan.id="sum";
      }
      oSpan.innerHTML = '发送请求的次数:' + sum;
      result.appendChild(oSpan);
      };
      if(sum%2){
      img.src = "http://7xpdkf.com1.z0.glb.clouddn.com/eg_bulboff.gif?sum="+sum;
      }else{
      img.src = "http://7xpdkf.com1.z0.glb.clouddn.com/eg_bulbon.gif?sum="+sum;
      }
      }
      </script>
  • script标签可以得到从其他来源数据,这也是JSONP依赖的根据

    • 缺点:只能发送get请求 ,无法访问服务器的响应文本(单向请求)。
JSONP跨域
  • JSONP(JSON with Padding)是数据格式JSON的一种“使用模式”,可以让网页从别的网域要数据。
  • 通过动态<script>元素使用,使用时为src指定一个跨域url。有两部分:1)回调函数:响应到来时在页面中使用;2)数据:传入回调函数中的JSON数据。
  • JSONP将访问跨域请求变成了执行远程JS代码,服务端不再返回JSON格式的数据,而是返回了一段将JSON数据作为传入参数的函数执行代码
  • 所有JSONP发送的get请求都是js类型,而非XHR。
  • 缺点:
    • 只能使用get请求。
    • 不能注册success、error等事件监听函数,不能很容易的确定JSONP请求是否失败。
    • JSONP是从其他域中加载代码执行,容易受到跨站请求伪造的攻击,其安全性无法确保。
服务器设置响应头, 允许跨域(一般不建议)
  • W3C推荐了一种新的跨域访问机制,即跨源资源共享(CORS)。可以让AJAX实现跨域访问。它允许一个域上的脚本向另一个域提交跨域 AJAX 请求。
  • 与 JSONP 不同,CORS 除了 GET 请求方法以外也支持其他的 HTTP 请求方法。服务器一般需要增加如下响应头的一种或几种:

    1
    2
    3
    4
    Access-Control-Allow-Origin: *
    Access-Control-Allow-Methods: POST, GET, OPTIONS
    Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
    Access-Control-Max-Age: 86400
  • 跨域请求默认不会携带Cookie信息,如果需要携带,请配置下述参数:

    1
    2
    3
    4
    5
    6
    7
    // 服务端
    "Access-Control-Allow-Credentials": true

    // 客户端需要设置withCredentials
    // Ajax设置
    var xhr = new XMLHttpRequest();
    xhr.withCredentials = true;
  • 跨源资源共享(CORS)是通过客户端 + 服务端协作声明的方式来确保请求安全的。

    • 服务端会在HTTP请求头中增加一系列HTTP请求参数(例如Access-Control-Allow-Origin等),来限制哪些域的请求和哪些请求类型可以接受。
    • 客户端在发起请求时必须声明自己的源(Orgin),否则服务器将不予处理,如果客户端不作声明,请求甚至会被浏览器直接拦截都到不了服务端。(对于支持CORS的浏览器,请求头会自动添加Origin,值为当前host)
    • 服务端收到HTTP请求后会进行域的比较,只有同域的请求才会处理。
修改document.domain跨子域
  • 前提条件:这两个域名必须属于同一个基础域名,而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域,所以只能跨子域。
  • 在根域范围内,允许把domain属性的值设置为它的上一级域。例如,在aaa.xxx.com域内,可以把domain设置为 xxx.com但不能设置为 xxx.org或者com
    现在存在两个域名aaa.xxx.com和bbb.xxx.com。在aaa下嵌入bbb的页面,由于其document.domain不一致,无法在aaa下操作bbb的js。可以在aaa和bbb下通过js将document.domain = ‘xxx.com’;设置一致,来达到互相访问的作用。
WebSocket
  • WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很棒的实现。
  • 需要注意:WebSocket对象不支持DOM 2级事件侦听器,必须使用DOM 0级语法分别定义各个事件。
后台代理
  • 同源策略是针对浏览器端进行的限制,可以通过服务器端来解决该问题。
  • 将后台作为代理,每次对其它域的请求转交给本域的后台,本域的后台通过模拟http请求去访问其它域,再将返回的结果返回给前台,但是不会携带cookie。
  • 即,DomainA客户端(浏览器)将对域B的请求转发给DomainA服务器DomainA服务器通过模拟http请求去访问DomainB服务器,然后将结果返回给DomainA客户端(浏览器)

JSONP的优缺点

优点
  • 不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制,JSONP可以跨越同源策略
  • 兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持。
  • 在请求完毕后可以通过调用callback的方式回传结果。将回调方法的权限给了调用方。这个就相当于将controller层和view层终于分开了。提供的jsonp服务只提供纯服务的数据,至于提供服务以后的页面渲染和后续view操作都由调用者自己定义。如果有两个页面需要渲染同一份数据,你们只需要有不同的渲染逻辑即可,逻辑都可以使用同一个jsonp服务。
缺点
  • 只支持GET请求,而不支持POST等其它类型的HTTP请求。
  • 只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
  • jsonp在调用失败时不会返回各种HTTP状态码。
  • 缺乏安全性。万一假如提供jsonp的服务存在页面注入漏洞,即它返回的javascript的内容被人控制的。结果是所有调用这个jsonp的网站都会存在漏洞。于是无法把危险控制在一个域名下,所以在使用jsonp的时候必须要保证使用的jsonp服务必须是安全可信的
------ 本文结束 ------