首页 > 代码库 > 跨域问题实践总结! 上(JSONP/document.domain/window.name)

跨域问题实践总结! 上(JSONP/document.domain/window.name)

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.com 和 y.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....中内嵌了一个 <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; // 框架的 document 对象iframe.contentWindow; // 框架的 window 对象

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

补充知识(绝对干货):
当两个页面不做任何处理,但是使用了框架或者 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-----------------这个方法有点问题,执行iframe.src之后iframe中的页面刷新后变量就消失了,取不到name的值阿??

我们来看以下一个场景:

随意打开一个页面,输入以下代码:

window.name = "My window‘s name";location.href = http://www.mamicode.com/"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....中内嵌了一个 <iframe>

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

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

var iframe = document.getElementById(‘iframe‘);var data = http://www.mamicode.com/‘‘;iframe.onload = function() {    data = http://www.mamicode.com/iframe.contentWindow.name;>

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

var iframe = document.getElementById(‘iframe‘);var data = http://www.mamicode.com/‘‘;iframe.onload = function() {    iframe.onload = function(){        data = http://www.mamicode.com/iframe.contentWindow.name;"hljs-string" style="box-sizing: border-box; color: #dd1144;">‘about:blank‘;};

或者将里面的 about:blank 替换成某个同源页面(最好是空页面,减少加载时间)。

补充知识:
about:blankjavascript: 和 data: 中的内容,继承了载入他们的页面的源。

这种方法与 document.domain 方法相比,放宽了域名后缀要相同的限制,可以从任意页面获取 string 类型的数据。

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.com 和 y.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....中内嵌了一个 <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; // 框架的 document 对象iframe.contentWindow; // 框架的 window 对象

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

补充知识(绝对干货):
当两个页面不做任何处理,但是使用了框架或者 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.mamicode.com/"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....中内嵌了一个 <iframe>

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

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

var iframe = document.getElementById(‘iframe‘);var data = http://www.mamicode.com/‘‘;iframe.onload = function() {    data = http://www.mamicode.com/iframe.contentWindow.name;>

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

var iframe = document.getElementById(‘iframe‘);var data = http://www.mamicode.com/‘‘;iframe.onload = function() {    iframe.onload = function(){        data = http://www.mamicode.com/iframe.contentWindow.name;"hljs-string" style="box-sizing: border-box; color: #dd1144;">‘about:blank‘;};

或者将里面的 about:blank 替换成某个同源页面(最好是空页面,减少加载时间)。

补充知识:
about:blankjavascript: 和 data: 中的内容,继承了载入他们的页面的源。

这种方法与 document.domain 方法相比,放宽了域名后缀要相同的限制,可以从任意页面获取 string 类型的数据。

跨域问题实践总结! 上(JSONP/document.domain/window.name)