1. 首先简单介绍一下什么是跨域
当我们在浏览器的地址栏中输入一个地址的时候,这个地址通常包含四部分信息内容。这四部分信息包含:①协议、②域名、③端口、④资源位置。
其中前三部分将会决定是否存在跨域,即:协议、域名、端口。如果其中有一个不一样,浏览器就会认为存在跨域的问题。例如:
域名1
|
域名2
|
是否有跨域
|
http://father.moxiao.com/a/a.html
|
http://father.moxiao.com/b/b.html
|
不存在跨域
|
https
://father.moxiao.com/a/a.html
|
http
://father.moxiao.com/b/b.html
|
存在跨域,协议不一样
|
http://
father
.moxiao.com/a/a.html
|
http://
child
.moxiao.com/b/b.html
|
存在跨域,域名不一样
|
http://father.moxiao.com/a/a.html
|
http://child.moxiao.com:
8082
/b/b.html
|
存在跨域,端口不一样
|
2.跨域的种类
一般有两种形式的跨域问题:
①使用
XmlHttpRequest
(
XHR
)或者使用
AJAX
发送的POST或者GET请求。这种形式的跨域是:
前端页面
与
后端
进行的跨域请求。这种形式跨域不在本篇文章的讨论范围内。
②
父子页面
之间进行的
DOM操作
(父子窗口之间的document操作)。这种形式的跨域是:前端页面与前端页面之间的通信或者相互操作的形成跨域。(
本文主要讨论就是这个话题
)
3.跨域的提示
如果在页面上出现了上面任何一种形式的跨域,一般会在浏览器的控制台(Chrome中按F12键)打印下面一段类似的描述:(一般包含accessing a cross-origin类似的信息就是跨域了)
二、问题描述
1.父子页面之间的DOM操作
在HTML页面中,我们有时候会使用<iframe>标签打开一个子窗口,又或者使用window.open方法打开一个子窗口,但有时想让父子窗口之间进行通信。例如:根据父窗口的大小,对<iframe>子窗口大小进行设置等问题。
①、如果<iframe>中src属性配置的地址和父窗口在
同一个域下
,可以在子窗口中通过
window.parent.document
来获取父窗口的DOM对象。亦或父窗口也可以通过
contentWindow
属性获取子窗口的DOM对象(Iframe情况下)。例如:document.getElementById('iframe标签的ID').contentWindow。两者之间操作不会出现任何跨域的问题,可以自由的获取任意想要的元素信息内容。也就不在本篇文章的讨论范围内。
②、如果<iframe>中的src属性配置的地址与父窗口不在同一个域下,即:协议、域名、端口任何一个不一样都会造成跨域问题。当使用
window.parent.document
进行操作的时候,就会提示跨域的问题,就是上面截图的信息内容。又或者使用
contentWindow
属性也会提示相同的跨域问题。有以下几种解决问题的方法以及这些方法的局限性。
三、解决页面之间跨域问题的方法
着重看第一和第二种方法,在第一种和第二种方法无法使用的时候,可以考虑剩下的方法。
1. 使用document.domain方法(
☆☆☆☆☆
)
1.1. document.domain的原理
JS在判断页面之间是否有跨域操作,主要根据页面的
document.domain
属性进行对比来判断是否有跨域。
如果两个页面的
document.domain
属性值一样的话,就算两个页面实际上不在同一个域下,JS也会认为他们之间没有跨域操作。这样的话,如果我们将两个页面的
document.domain
属性值设置为一样的。不同域下的页面之间操作就可以像在一个域名下的页面之间的操作。
document.domain
的默认值为:
当前域名
。
1.2. document.domain的局限性
上面说如果将两个页面的document.domain属性设置为一样的话,就算实际上不在同一个域下,他们也可以进行DOM对象之间的操作。但是
document.domain
也不是你想设置成什么就可以设置成什么的,这个属性的属性值有一定的设置规则,如果不满足设置的规则就不能设置成功。
规则如下:
①document.domain只设置
域名或者IP地址
。即:没有协议(http/https),没有端口。
②如果访问的地址是域名形式,则
document.domain
只能设置为当前域名下的
同级或者父级域名
,并且
不能
设置为
顶级域名
,
也不可以设置为其他形式的域名(不是当前域名的父级或同级)。
例如:当前访问地址为:https://mp.csdn.net/mp_blog/creation/editor/121540023(当前页面)。可以打开Chrome的控制台(F12)找到console的tab。默认情况下,我们输入document.domain会显示
‘csdn.net’
。其中csdn.net就是https://mp.csdn.net的父级域名。我们也可以设置为mp.csdn.net。因为mp.csdn.net是mp.csdn.net的同级域名。
但是我们不能设置顶级域名,即document.domain='net',就会提示错误信息。也不能设置其他的域名形式。即document.domain='
csdn1.
net'或者document.domain='
baidu.com
'。提示也说得很清楚。必须是mp.csdn.net的一个后缀形式。
③如果访问地址是IP的形式,则document.domain只能设置一样的IP地址。
下图就是在当前页面进行的设置。
注意:根据上面的设置规则,在使用document.domain的方式时,页面之间必须有相同的父级域名。否则这种方式将无法进行操作。
-
document.domain只配置域名或者IP地址,不需要配置协议、端口。
-
因为document.domain只配置的域名或者IP地址,所以页面之间的协议必须是一样的,要么都是HTTP,要么都是HTTPS。如果是混合的,这种方式也是不行的。但是页面端口可以不一样。
1.3. domian方式的演示示例
https://github.com/1997chang/iframeCrossAccess
是一个进行页面操作跨域的显示示例。该项目有两个模块,fathermodule和childmodule。
在
static/domain
文件夹下显示的为document.domain的解决方案的示例。
启动father以及child模块,在地址栏中输入:father.moxiao.com:8081/domain/fatherDomainMethod.html。就可以进行页面之间的相互操作了。
2. 使用postMessage方式(
☆☆☆☆☆
)
在H5之后,window对象添加了一个
postMessage
的方法,使用postMessage方法可以向
任何域名
发送消息内容(字符串或者对象)。postMessage方法发送的消息将不会受到跨域的影响。
2.1 postMessage不受跨域的影响
在HTML页面中,可以在当前页面中使用
window.parent
获取父级窗口对象(WIndow),从而向父级窗口发送消息(
子->父
)。同样也可以使用
contentWindow
获取Iframe的打开的子窗口对象,从而向子窗口发送消息。(
父->子
)
在父窗口中获取其子窗口的window对象,又或者在子窗口中获取其父窗口的window对象。但是如果两个父子窗口存在跨域问题,那么将不能对
获取到的window对象
进行任何操作。即可以在跨域的情况下获取其子窗口或者父窗口的window对象,但是不能对获取到的window对象进行任何操作。如果进行了任何的操作,都会提示跨域的问题。但是
有两个是例外
。即使在跨域的情况下也可以对其进行设置。
①
location属性
:设置window准备跳转的页面。后面两种解决方法使用该属性。
②
postMessage方法
,H5之后添加的。
无论当前域名是什么,获取到的window对象的域名是什么。即使这两个域名之间存在跨域问题,在window对象中
有一个属性location
的设置和
一个方法postMessage
的调用不会受到跨域的影响。
2.2. postMessage方法的使用
postMessage和addEventListener方法配合使用。postMessage是用来发送消息的,addEventListener是用来接收消息时候的回调。
postMessage
方法有两个参数:
①发送的消息:可以是字符串,也可以是对象。
②接收消息的域名:
要么是具体值(协议、域名、端口都要设置,且相同),要么是*
。如果设置为*:则表示没有任何限制。如果设置为具体值的话,则必须与
发送到的窗口
域名相同,包含协议、域名、端口,如果有一个不同的话,就会发送不出去。
例如:parent.window.postMessage('父窗口你好','
http://father.moxiao.com
'),就会提示错误。因为
parent.window
窗口的域名为:
http://father.moxiao.com:8082
,而发送配置的域名是:
http://father.moxiao.com
。就会提示下面的错误信息:
parent.window.postMessage(content, 'http://father.moxiao.com:8081');
addEventListener
方法有两个参数:
①监听的时间类型:对于发送消息而言,固定为message。
②回调函数:在回调函数中一个参数event,event其中包含三个重要的属性:
1.data:表示接受到的消息内容,可能是字符串,可能是对象,取决有发送方。
2.origin:String类型,表示发送消息的域名地址
3.source:对象,表示发送消息的窗口对象。
window.addEventListener('message', (event) => {
$("#child").text($("#child").text() + '由' + event.origin + "发送过来的消息为:"+event.data);
2.3. postMessage的演示示例:
https://github.com/1997chang/iframeCrossAccess中static下的postMessage文件夹下。启动项目之后,在地址栏中访问:http://father.moxiao.com:8081/postmessage/fatherPostmessage.html地址,就可以进行父子窗口发送消息。显示如下:
3.使用window.name属性(☆☆☆)
window.name用于表示当前窗口的名称。可以容纳2M的大小内容。注意:在跨域的情况下,不能设置window.name的属性值,也不能获取其中的属性值。一般window.name属性要配合window.location属性进行操作。
3.1. window.name属性进行消息传递的原理
window.name表示当前窗口的名称。一个窗口在没有关闭之前,可以在地址栏中进行多次地址改变。但是他们都共享同一个window.name属性值。即只要当前窗口不关闭,无论进行多少次地址跳转,这些地址是否存在跨域都没有关系,window.name将保存最后一次窗口的设置的值。
你也可以自行体验一把:打开Chrome的控制台,当前地址为:www.baidu.com。你在console的控制台中输入window.name='moxiao',然后将当前页面的地址栏的地址改为:mai.qq.com,然后在console的控制台中打印window.name将会显示:‘moxiao’。
window.name的值可以在多个跨域地址中持久化,这也就是跨域父子窗口可以进行消息传递的基石。但是window.name属性的设置和获取是不能跨域进行操作的。如果父窗口想设置子窗口的window.name的属性值,必须在同一个域下。父窗口向获取子窗口的window.name的属性值,也必须在同一个域名下。
当知道window.name属性可以在多个页面之间进行持久化的这个机制后,我们就会想象一下如何进行跨域的父子窗口传递消息。
当父窗口想向子窗口传递消息时:我们在父窗口的项目中添加一个代理页面(proxy.html),因为父窗口和代理页面在同一个项目下,所以不存在跨域的问题,这时候就可以在父窗口对代理窗口的window.name设置要传递的消息内容,设置完成之后,让代理页面进行window.location跳转到子窗口中,因为是同一个窗口,所以window.name属性将会持久化到子窗口中。这样就可以在子窗口获取当前窗口的window.name属性值,也就是父窗口想要传递的消息。
3.2. window.name的演示示例
https://github.com/1997chang/iframeCrossAccess中static下的name文件夹下。启动项目之后,在地址栏中访问:http://father.moxiao.com:8081/name/fatherName.html地址,就可以看到父窗口向子窗口发送的”moxiao“的消息内容了。
4.使用hashtag的方式(☆☆☆)
4.1. hashtag介绍以及原理
hashtag是地址栏中#之后的部分。注意:hash是地址栏的最后一部分。地址栏中传递的参数必须在#之前,否则会将他们当成是hashtag的一部分,而不是传递的参数了。
hashtag的一个显著的特征就是:hashtag的改变不会对页面进行重新加载。即不会发送一个HTTP请求。当我们在地址栏中输入一个地址时,会向服务器发送HTTP请求。当改变地址栏传递的参数时(?后面的参数),会重新发送HTTP请求。但是设置或者改变hashtag不会重新发送HTTP请求。给人直观的印象就是地址栏中的地址改变了,然后就没有任何其他的操作了。
可以使用window.location.hash的方式获取。注意:获取到的hash值是包含#号的。
可以使用window.onhashchange事件监听hash值的改变。
4.2. hashtag的演示示例
https://github.com/1997chang/iframeCrossAccess中static下的hash文件夹下。启动项目之后,在地址栏中访问:http://father.moxiao.com:8081/hash/fatherHash.html地址,就可以通过父窗口向子窗口发送消息了。
并且点击发送按钮,不会进行任何的页面刷新,重新加载、发送HTTP请求什么的。
本篇文章主要介绍父子页面DOM操作造成的跨域问题,讲述了四种解决方法。其中第一种和第二种方法最为常见。最后再考虑使用window.name和hashtag的方式进行消息的传递。
四种方法的项目地址为:https://github.com/1997chang/iframeCrossAccess
获取 子页面 的 window 对象 ,即获取了子页面的所有变量和方法,获取方法如下:
// iframe id
document.getElementById('menuIframe').contentWindow
// iframe name
window.frames['menuIframe'].window
// iframe index 当前窗体的第几个 iframe
window.frames[1].window
2、子 iframe 获取 父
方法调用
父页面调用子页面方法:FrameName.window.childMethod();
子页面调用父页面方法:parent.window.parentMethod();
parent.html
<html>
<head>
<script type="text/javascript...
onload加载完成再进行操作
子页面延时获取
场景是这样的,父级页面嵌套了多个iframe,report_six.html子级页面按需加载数量,且每个子页面渲染不同的数据。
父页面for循环加载子页面数量,动态生成id,根据对应的id,传值给对应的子级页面元素,一定要写在onload里面
下面是父页面
当我们在一个网页中使用iframe标签引入另一个网页时,如果这两个网页的域名不同,就会出现跨域问题。此时,如果需要在iframe中调用父页面的方法,就需要解决跨域问题。
解决跨域问题的四种方式:
1. JSONP
JSONP是一种通过动态创建script标签的方式,从另一个域名下获取数据并进行回调的方法。JSONP只支持GET请求,而且只能获得JSON格式的数据。
2. CORS
CORS是一种浏览器的安全策略,允许网页从不同的域名访问服务器的资源。在服务器端设置CORS响应头,可以允许跨域访问。但是CORS只支持现代浏览器,IE8及以下版本不支持。
3. 代理
代理是指在同一个域名下,通过服务器端转发请求的方式实现跨域访问。当客户端发起请求时,先将请求发送给同域名下的某个接口,接口再将请求转发给另一个域名下的接口,最后将数据返回给客户端。
4. postMessage
postMessage是HTML5中新增的API,可以在不同的窗口或iframe之间传递数据。通过在父页面和iframe页面分别注册message事件,并在事件处理函数中进行数据传递,就可以实现跨域通信。postMessage可以传输任意格式的数据,但需要注意数据的安全性。
解决java连接SQLSERVER数据库之驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。错误:Could not generate DH keypair”。
54726