[聚合文章] 关于跨域

JSONP 2016-09-14 11 阅读

前言

转眼就是秋招季啦。经历了几场笔试面试,屡次被问到关于如何实现跨域。老实说,之前都是纸上谈兵,也没有项目需要跨域,甚至觉得这个东西没什么意义。直到今天项目中遇到了跨域问题,看了不少资料才理解跨域的普遍性和意义。特写此篇文章整理自己所得。

本文来源个人博客: 关于跨域

什么是跨域

同源策略

说到跨域就不得不提“同源策略”。

同源策略是Web浏览器针对恶意的代码所进行的措施,为了防止世界被破坏,为了保护世界的和平,Web浏览器,采取了同源策略,只允许脚本读取和所属文档来源相同的窗口和文档的属性。

那么,怎么判断文档来源是否相同呢?很简单,看三个部分: 协议主机端口号 。只要其中一个部分不同,则不同源。

跨域的应用场景

  1. 来自 home.example.com 的文档里的脚本读取 developer.example.com 载入的文档的属性。

  2. 来自 home.example.com 的文档里的脚本读取 text.segmentfault.com 载入的文档的属性

如何跨域

设置 domain 属性

针对上述应用场景的第一种情况,可以设置 Document 对象的 domain 属性。但是设置时使用的字符串必须具有有效的域前缀或者它本身。

PS: domain 值中必须有一个点号。

PS: domain 不能由松散的变为紧绷的。

//初始值 "home.example.com" 
document.domain = "example.com"; //OK
document.domain = "home.example.com"; //NO,不能由松散变紧绷
document.domain = "example"; //NO,必须有一个点号
document.domain = "another.com"; //NO, 必须是有效域前缀或其本身

JSONP

JSONP由两部分组成: 回调函数和数据。

原理:通过动态 <script> 元素来使用,可以通过 src 属性指定一个跨域URL。

function handler(data){
    console.log(data);
}

var script = document.createElement("script");
script.src = "https://segmentfault.com/json/?callback=handler";
document.body.insertBefore(script, document.body.firstChild);

除此之外,还可以使用 $.ajax 来实现,代码来自于 http://www.tuicool.com/articl...

//通过$.ajax来实现的jsonp
 $.ajax({
    url: 'https://segmentfault.com/json',
    data: data,
    dataType: 'jsonp',
    // jsonp 字段含义为服务器通过什么字段获取回调函数的名称
    jsonp: 'callback',
    // 声明本地回调函数的名称,jquery 默认随机生成一个函数名称
    jsonpCallback: 'jsonpCallback',
    success: function(data) {
        console.log("ajax success callback: " + data.name)
    },
    error: function(jqXHR, textStatus, errorThrown) {
        console.log(textStatus + ' ' + errorThrown);
    }
});

优点:

  1. 兼容性强。

  2. 简单易用,能之间访问响应文本,支持在浏览器与服务器之间双向通信。

不足:

  1. 只能用 GET 方法,不能使用 POST 方法

  2. 无法判断请求是否失败,没有错误处理。

跨域资源共享CORS

需要浏览器和服务器同时支持。

原理:使用 "Origin:" 请求头和 "Access-Control-Allow-Origin" 响应头来扩展HTTP。其实就是利用新的HTTP头部来进行浏览器与服务器之间的沟通。

针对前端代码而言,变化的地方在于 相对路径需改为绝对路径

//以前的方式
var xhr = new XMLHttpRequest(); 
xhr.open("GET", "/test", true); 
xhr.send(); 
//CORS方式
var xhr = new XMLHttpRequest(); 
xhr.open("GET", "http://segmentfault.com/test", true); 
xhr.send();

针对服务器代码而言,需要设置 Access-Control-Allow-Origin ,显式地列出源或使用通配符来匹配所有源。

优点:

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

  2. 使用CORS,开发者可以使用普通的 XMLHttpRequest 发起请求和获得数据

不足:

  1. 不能发送和接收 cookie

  2. 不能使用 setRequestHeader() 设置自定义头部

  3. 兼容IE10+

postMessage

postMessge() 是HTML5新定义的通信机制。所有主流浏览器都已实现。该API定义在Window对象。

otherWindow.postMessage(message, targetOrigin);

message : 要传递的消息。

targetOrigin : 指定 目标窗口 的源。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。这个机制用来控制消息可以发送到哪些窗口;

当源匹配时,调用 postMessage() 方法时,目标窗口的Window对象会触发一个 message 事件。在进行监听事件时,应先判断 origin 属性,忽略来自未知源的消息。

//<http://example.com:8080>上的脚本:
var popup = window.open(...popup details...);
popup.postMessage("The user is 'bob' and the password is 'secret'",
              "https://secure.example.net");  
popup.postMessage("hello there!", "http://example.org");

function receiveMessage(event)
{
  if (event.origin !== "http://example.org")
    return;
  // event.source is popup
  // event.data is "hi there yourself!  the secret response is: rheeeeet!"【见下面一段代码可知】
}
window.addEventListener("message", receiveMessage, false);

针对上面的脚本进行接受数据的操作:

/*
 * popup的脚本,运行在<http://example.org>:
 */

//当postMessage后触发的监听事件
function receiveMessage(event)
{
  //先判断源
  if (event.origin !== "http://example.com:8080")
    return;

  // event.source:window.opener
  // event.data:"hello there!"

  event.source.postMessage("hi there yourself!  the secret response " +
                           "is: rheeeeet!",
                           event.origin);
}

window.addEventListener("message", receiveMessage, false);

注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。