实现跨域请求的八种方式

 

前端开发中咱们常常会遇到跨域请求的状况,处理跨域请求方式不少大概分为八种。html

浏览器的同源策略

提到跨域不能不先说一下”同源策略”。
​何为同源?只有当协议、端口、和域名都相同的页面,则两个页面具备相同的源。只要网站的 协议名protocol、 主机host、 端口号port 这三个中的任意一个不一样,网站间的数据请求与传输便构成了跨域调用,会受到同源策略的限制。
​ 同源策略限制从一个源加载的文档或脚本如何与来自另外一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不一样域的服务进行跨站调用(一般指使用XMLHttpRequest请求)。前端

特别注意两点:
一、若是是协议和端口形成的跨域问题“前台”是无能为力的;
二、在跨域问题上,域仅仅是经过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
URL的首部:指window.location.protocol +window.location.host,也能够理解为“Domains(域名), protocols(协议) and ports(端口) must match”。html5

跨域请求方式

​ 解决跨域问题,最简单的莫过于经过nginx反向代理进行实现,可是其须要在运维层面修改,且有可能请求的资源并再也不咱们控制范围内(第三方),因此该方式不能做为通用的解决方案,下面阐述了常常用到几种跨域方式:nginx


方式一:图片ping或script标签跨域

图片ping经常使用于跟踪用户点击页面或动态广告曝光次数。
script标签能够获得从其余来源数据,这也是JSONP依赖的根据。
缺点:只能发送Get请求 ,没法访问服务器的响应文本(单向请求)跨域

方式二:JSONP跨域

​ JSONP(JSON with Padding)是数据格式JSON的一种“使用模式”,可让网页从别的网域要数据。根据 XmlHttpRequest 对象受到同源策略的影响,而利用 <script>元素的这个开放策略,网页能够获得从其余来源动态产生的JSON数据,而这种使用模式就是所谓的 JSONP。用JSONP抓到的数据并非JSON,而是任意的JavaScript,用 JavaScript解释器运行而不是用JSON解析器解析。全部,经过Chrome查看全部JSONP发送的Get请求都是js类型,而非XHR。浏览器

缺点:安全

  • 只能使用Get请求
  • 不能注册success、error等事件监听函数,不能很容易的肯定JSONP请求是否失败
  • JSONP是从其余域中加载代码执行,容易受到跨站请求伪造的攻击,其安全性没法确保

方式三:使用跨域资源共享(CORS)来跨域

Cross-Origin Resource Sharing(CORS)跨域资源共享是一份浏览器技术的规范,提供了 Web 服务从不一样域传来沙盒脚本的方法,以避开浏览器的同源策略,确保安全的跨域数据传输。现代浏览器使用CORS在API容器如XMLHttpRequest来减小HTTP请求的风险来源。与 JSONP 不一样,CORS 除了 GET 要求方法之外也支持其余的 HTTP 要求。服务器通常须要增长以下响应头的一种或几种:服务器

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信息,若是须要携带,请配置下述参数:app

 
 
"Access-Control-Allow-Credentials": true
// Ajax设置
"withCredentials": true
 
 

 

 
 

方式四:window.name+iframe

​ window.name经过在iframe(通常动态建立i)中加载跨域HTML文件来起做用。而后,HTML文件将传递给请求者的字符串内容赋值给window.name。而后,请求者能够检索window.name值做为响应。运维

iframe标签的跨域能力;
window.name属性值在文档刷新后依旧存在的能力(且最大容许2M左右)。
每一个iframe都有包裹它的window,而这个window是top window的子窗口。contentWindow属性返回<iframe>元素的Window对象。你可使用这个Window对象来访问iframe的文档及其内部DOM。

<!-- 
 下述用端口 
 10000表示:domainA
 10001表示:domainB
-->

<!-- localhost:10000 -->
<script>
  var iframe = document.createElement('iframe');
  iframe.style.display = 'none'; // 隐藏

  var state = 0; // 防止页面无限刷新
  iframe.onload = function() {
      if(state === 1) {
          console.log(JSON.parse(iframe.contentWindow.name));
          // 清除建立的iframe
          iframe.contentWindow.document.write('');
          iframe.contentWindow.close();
          document.body.removeChild(iframe);
      } else if(state === 0) {
          state = 1;
          // 加载完成,指向当前域,防止错误(proxy.html为空白页面)
          // Blocked a frame with origin "http://localhost:10000" from accessing a cross-origin frame.
          iframe.contentWindow.location = 'http://localhost:10000/proxy.html';
      }
  };

  iframe.src = 'http://localhost:10001';
  document.body.appendChild(iframe);
</script>

<!-- localhost:10001 -->
<!DOCTYPE html>
...
<script>
  window.name = JSON.stringify({a: 1, b: 2});
</script>
</html>

 

直接嵌入其余域(localhots:10001)下的URL会报错,因此须要加载完成替换为当前域的URL(localhots:10000),proxy.html为空白页面,只为解决该问题;

从新设置src(http://localhost:10000/proxy.html)后致使页面不断刷新,因此经过state来控制;
所有获取完结果后,清除该iframe。


方式五:window.postMessage()(不经常使用)

window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可使用它来向其它的window对象发送消息,不管这个window对象是属于同源或不一样源(可实现跨域),目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。
message:为要发送的消息,类型只能为字符串;
targetOrigin:用来限定接收消息的那个window对象所在的域,若是不想限定域,可使用通配符 “*”。

1)建立www.test.com/a.html页面代码:

 
 
<script>
function onLoad(){
    var iframe = document.getElementById("iframe");
    var win = iframe.contentWindow;
    win.postMessage('哈哈,我是来自页面a.html的信息哟!','*');//向不一样域的www.script.com/b.html发送消息
}
</script>
<iframe id="iframe" src="www.script.com/b.html" onload="onLoad()"></iframe>
 
 

 

 
 

2)建立www.script.com/b.html页面代码:

<script>
window.onmessage = function(e){//注册message时间来接收消息
    e = e || event;            //获取时间对象
    alert(e.data);             //经过data属性来获得传送的消息
}
</script>

 

优势:使用postMessage来跨域传送数据仍是比较直观和方便的;
缺点: IE六、IE7不支持,因此用不用还得根据实际须要来决定。


方式六:经过document.domain+iframe来跨子域(必须主域相同)

前提条件:这两个域名必须属于同一个基础域名!并且所用的协议,端口都要一致,不然没法利用document.domain进行跨域,因此只能跨子域

​ 在根域范围内,容许把domain属性的值设置为它的上一级域。例如,在”aaa.xxx.com”域内,能够把domain设置为 “xxx.com” 但不能设置为 “xxx.org” 或者”com”。

​ 如今存在两个域名aaa.xxx.com和bbb.xxx.com。在aaa下嵌入bbb的页面,因为其document.name不一致,没法在aaa下操做bbb的js。能够在aaa和bbb下经过js将document.name = 'xxx.com';设置一致,来达到互相访问的做用。


方式七:WebSocket

WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通讯,同时容许跨域通信,是server push技术的一种很棒的实现。相关文章,请查看:WebSocket、WebSocket-SockJS

须要注意:WebSocket对象不支持DOM 2级事件侦听器,必须使用DOM 0级语法分别定义各个事件。

 


方式八:代理

同源策略是针对浏览器端进行的限制,能够经过服务器端来解决该问题

DomainA客户端(浏览器) ==> DomainA服务器 ==> DomainB服务器 ==> DomainA客户端(浏览器)

实现HTTP、HTTPS代理请参照: 建立HTTP与HTTPS服务器与客户端

相关文章
相关标签/搜索