再谈前端跨域

1. JSONP

首先要介绍的跨域方法必然是 JSONP。
现在你想要获取其他网站上的 JavaScript 脚本,你非常高兴的使XMLHttpRequest 对象来获取。但是浏览器一点儿也不配合你,无情的弹出了下面的错误信息:

XMLHttpRequest cannot load http://x.com/main.dat. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://y.com' is therefore not allowed access.

你心里肯定会想,我难道要用后台做个爬虫来获取这个数据吗?!为了避免这种事情发生,JSONP 就派上用场了。
<script>标签是不受同源策略的限制的,它可以载入任意地方的 JavaScript 文件,而并不要求同源。所以 JSONP 的理念就是,我和服务端约定好一个函数名,当我请求文件的时候,服务端返回一段 JavaScript。这段 JavaScript 调用了我们约定好的函数,并且将数据当做参数传入。非常巧合的一点(其实并不是),JSON 的数据格式和JavaScript 语言里对象的格式正好相同。所以在我们约定的函数里面可以直接使用这个对象。光说不练假把式,让我们来看一个例子:
你需要获取数据的页面 index.html:

<script>   
  function getWeather(data) {       
    console.log(data);
  }
</script>
<script src="http://x.y.com/xx.js">

http://x.y.com/xx.js 文件内容:

getWeather({
  "城市": "北京",
  "天气": "大雾"
});

我们可以看到,在我们定义了 getWeather(data)这个函数后,直接载入了 xx.js。在这个脚本中,执行了 getWeather函数,并传入了一个对象。然后我们在这个函数中将这个对象输出到 console 中。

这就是整个 JSONP 的流程。

2. document.domain

使用条件:

  1. 有其他页面 window对象的引用
  2. 二级域名相同
  3. 协议相同
  4. 端口相同

document.domain默认的值是整个域名,所以即使两个域名的二级域名一样,那么他们的 document.domain也不一样。使用方法就是将符合上述条件页面的 document.domain设置为同样的二级域名。这样我们就可以使用其他页面的 window对象引用做我们想做的任何事情了。
补充知识:
x.one.example.comy.one.example.com 可以将 document.domain设置为 one.example.com,也可以设置为example.com。document.domain只能设置为当前域名的一个后缀,并且包括二级域名或以上(.edu.cn这种整个算顶级域名)。我们直接操刀演示,用两个网站 http://wenku.baidu.com/http://zhidao.baidu.com/。这两个网站都是 http 协议,端口都是 80, 且二级域名都是 baidu.com。打开http://wenku.baidu.com/,在 console 中输入代码:

document.domain = 'baidu.com';
var otherWindow = window.open('http://zhidao.baidu.com/');

我们现在已经发现百度知道的网页已经打开了,在百度知道网页的 console 中输入以下代码:

document.domain = 'baidu.com';

现在回到百度文库的网页,我们就可以使用百度知道网页的 window对象来操作百度知道的网页了。例如:

var divs = otherWindow.document.getElementsByTagName('div');

上面这个例子的使用方法并不常见,但是非常详细的说明了这种方法的原理。这种方法主要用在控制 <iframe>的情况中。
比如我的页面(http://one.example.com/index.html)中内嵌了一个 <iframe>:

<iframe id="iframe" src="http://two.example.com/iframe.html"></iframe>

我们在 iframe.html 中使用 JavaScript 将 document.domain设置好,也就是 example.com。在 index.html 执行以下脚本:

var iframe = document.getElementById('iframe');
document.domain = 'example.com';
iframe.contentDocument; // iframe的 document 对象
iframe.contentWindow; // iframe的 window 对象

这样,我们就可以获得对iframe的完全控制权了。

补充知识:
当两个页面不做任何处理,但是使用了iframe或者 window.open() 得到了某个页面的 window 对象的引用,我们可以直接访问的属性有哪些?

方法
window.blur
window.close
window.focus
window.postMessage
window.location.replace
属性 权限
window.closed 只读
window.frames 只读
window.length 只读
window.location.href 只写
window.opener 只读
window.parent 只读
window.self 只读
window.top 只读
window.window 只读

3. window.name

我们来看以下一个场景:
随意打开一个页面,输入以下代码:

window.name = "My window's name";
location.href = "http://www.qq.com/";

再检测 window.name :

window.name; // My window's name

可以看到,如果在一个标签里面跳转网页的话,我们的 window.name是不会改变的。基于这个思想,我们可以在某个页面设置好 window.name的值,然后跳转到另外一个页面。在这个页面中就可以获取到我们刚刚设置的 window.name了。由于安全原因,浏览器始终会保持 window.name 是 string类型。这个方法也可以应用到与 <iframe> 的交互上来。我的页面(http://one.example.com/index.html)中内嵌了一个 <iframe>:

<iframe id="iframe" src="http://omg.com/iframe.html"></iframe>

在 iframe.html 中设置好了 window.name为我们要传递的字符串。我们在 index.html 中写了下面的代码:

var iframe = document.getElementById('iframe');
var data = '';
iframe.onload = function() {   
  data = iframe.contentWindow.name;
};

定睛一看,为毛线报错?细心的读者们肯定已经发现了,两个页面完全不同源啊!由于 window.name 不随着 URL 的跳转而改变,所以我们使用一个暗黑技术来解决这个问题:

var iframe = document.getElementById('iframe');
var data = '';
iframe.onload = function() {    
  iframe.onload = function() {        
    data = iframe.contentWindow.name;    
  }   
   iframe.src = 'about:blank';
};

或者将里面的 about:blank 替换成某个同源页面(最好是空页面,减少加载时间)。
补充知识:
about:blank,javascript: 和 data:中的内容,继承了载入他们的页面的源。
这种方法与 document.domain方法相比,放宽了域名后缀要相同的限制,可以从任意页面获取 string类型的数据。

4. [HTML5] postMessage

在 HTML5 中, window 对象增加了一个非常有用的方法:

windowObj.postMessage(message, targetOrigin);

windowObj: 接受消息的 Window 对象。
message: 在最新的浏览器中可以是对象。
targetOrigin: 目标的源,* 表示任意。

这个方法非常强大,无视协议,端口,域名的不同。
下面是烤熟的栗子:

var windowObj = window; // 可以是其他的 Window 对象的引用
var data = null;
addEventListener('message', function(e) {   
  if(e.origin == 'http://qiaohongshen.github.io/foo') {      
    data = e.data;
    e.source.postMessage('Got it!', '*');
  }
});

message事件就是用来接收 postMessage发送过来的请求的。函数参数的属性有以下几个:
origin: 发送消息的 window的源。
data: 数据。
source: 发送消息的 Window对象。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容

  • 1. 什么是跨域? 跨域一词从字面意思看,就是跨域名嘛,但实际上跨域的范围绝对不止那么狭隘。具体概念如下:只要协议...
    w_zhuan阅读 508评论 0 0
  • 1. 什么是跨域? 跨域一词从字面意思看,就是跨域名嘛,但实际上跨域的范围绝对不止那么狭隘。具体概念如下:只要协议...
    他在发呆阅读 822评论 0 0
  • 跨域资源共享 CORS 对于web开发来讲,由于浏览器的同源策略,我们需要经常使用一些hack的方法去跨域获取资源...
    默默先生Alec阅读 582评论 0 0
  • 来吧,少年,今天还能看文章学习的,一多半都是单身贵族,看朋友圈还会被虐,不如学习,上街还会被虐,不如学习,痛并快乐...
    范小饭_阅读 7,895评论 3 24
  • 唉…… 可能是我沉迷于手机无法自拔,来了辆公交车就上,上车后我还纳闷呢,“咦,是坐这趟公交的人太多,公交公司买新车...
    大发花儿阅读 262评论 1 1