跨域:利用iframe实现跨域DOM互访的四种方式

注:跨域的知识点详见:跨域相关知识点

目录

实验验证环境配置:

1、利用document.domain降域

方法1:

方法2:

2、利用location.hash

3、利用window.name

4、利用postMessage(最推荐)

使用postmessage实现跨域访问

使用postmessage读取其他窗口的localstorage(普通款)

使用postmessage读取其他窗口的localstorage(加强版本):


实验验证环境配置:

我们一共需要配置五个虚拟主机,即需要使用五个域名

www.aaa.com、www.bbb.com、master.security.com、slave.security.com、www.security.com

(1)在本地的WWW目录下创建两个文件夹

cross_orgin和cross_orgin_sub

(2)域名分配:

cross_orgin:www.aaa.com

cross_orgin_sub:www.bbb.com

为了方便我们访问,这里需要在本地配置虚拟主机

(3)虚拟主机:


 DocumentRoot "F:/PHPstudy/phpstudy_pro/WWW/openlab/cross_orgin"
 ServerName  www.aaa.com
 FcgidInitialEnv PHPRC "F:/PHPstudy/phpstudy_pro/Extensions/php/php7.3.4nts"
 AddHandler fcgid-script .php
 FcgidWrapper "F:/PHPstudy/phpstudy_pro/Extensions/php/php7.3.4nts/php-cgi.exe" .php
 
        Options FollowSymLinks ExecCGI
        AllowOverride All
        Order allow,deny
        Allow from all
        Require all granted
        DirectoryIndex index.php index.html


 DocumentRoot "F:/PHPstudy/phpstudy_pro/WWW/openlab/cross_orgin_sub"
 ServerName www.bbb.com
 FcgidInitialEnv PHPRC "F:/PHPstudy/phpstudy_pro/Extensions/php/php7.3.4nts"
 AddHandler fcgid-script .php
 FcgidWrapper "F:/PHPstudy/phpstudy_pro/Extensions/php/php7.3.4nts/php-cgi.exe" .php
 
        Options FollowSymLinks ExecCGI
        AllowOverride All
        Order allow,deny
        Allow from all
        Require all granted
        DirectoryIndex index.php index.html

 这里配置了两个虚拟主机:www.aaa.com 和 www.bbb.com分别对应的是我们所配置的

cross_orgin和cross_orgin_sub两个目录文件夹

(4)在vhost中配置ip与域名的对应关系

然后我们需要在本地环境的C:\Windows\System32\drivers\etc 目录下的vhost文件中增加对应关系

127.0.0.1 www.aaa.com
127.0.0.1 www.bbb.com

(5)我们可以在 cross_orgin和cross_orgin_sub 文件夹中任意创建页面文件,然后尝试使用域名进行访问

跨域:利用iframe实现跨域DOM互访的四种方式_第1张图片

跨域:利用iframe实现跨域DOM互访的四种方式_第2张图片

如果使用域名访问看到了编辑的页面内容,说明我们的环境已经搭建好了。

下面就详细的介绍一下ifame实现跨域DOM互访问的四种方式:

(6)

然后使用上面同样的方式完成master.security.com和slave.security.com和www.security,com这三个域名的搭建

这三个域名对应的是本地的cookie_orgin和cookie_orgin_sub、CSSinject文件夹

这里就只提供这三个域名的虚拟主机配置文件:


 DocumentRoot "F:/PHPstudy/phpstudy_pro/WWW/openlab/cookie_orgin"
 ServerName  master.security.com
 FcgidInitialEnv PHPRC "F:/PHPstudy/phpstudy_pro/Extensions/php/php7.3.4nts"
 AddHandler fcgid-script .php
 FcgidWrapper "F:/PHPstudy/phpstudy_pro/Extensions/php/php7.3.4nts/php-cgi.exe" .php
 
        Options FollowSymLinks ExecCGI
        AllowOverride All
        Order allow,deny
        Allow from all
        Require all granted
        DirectoryIndex index.php index.html




 DocumentRoot "F:/PHPstudy/phpstudy_pro/WWW/openlab/cookie_orgin_sub"
 ServerName slave.security.com
 FcgidInitialEnv PHPRC "F:/PHPstudy/phpstudy_pro/Extensions/php/php7.3.4nts"
 AddHandler fcgid-script .php
 FcgidWrapper "F:/PHPstudy/phpstudy_pro/Extensions/php/php7.3.4nts/php-cgi.exe" .php
 
        Options FollowSymLinks ExecCGI
        AllowOverride All
        Order allow,deny
        Allow from all
        Require all granted
        DirectoryIndex index.php index.html




 DocumentRoot "F:/PHPstudy/phpstudy_pro/WWW/openlab/CSSinject"
 ServerName  www.security.com
 FcgidInitialEnv PHPRC "F:/PHPstudy/phpstudy_pro/Extensions/php/php7.3.4nts"
 AddHandler fcgid-script .php
 FcgidWrapper "F:/PHPstudy/phpstudy_pro/Extensions/php/php7.3.4nts/php-cgi.exe" .php
 
        Options FollowSymLinks ExecCGI
        AllowOverride All
        Order allow,deny
        Allow from all
        Require all granted
        DirectoryIndex index.php index.html

注:虚拟主机中的目录文件需要与自己本地的文件配置一致,不要直接使用我这个 

1、利用document.domain降域

方法1:

master.security.com 的index.html文件:

方法1:


​

    
    
    
    Master

​

    

master

     

这里将slave.security通过iframe嵌入到当前页面,并且这里在script标签中设置了document.domain,目的是让同样设置了这样的页面可以跨域互访,并还设置了一个document.cookie用于测试

    document.domain = 'security.com'//domain进行降域不看子域名只看主域
    document.cookie = 'name=master'

slave.security.com的index.html文件 : 



​

    
    
    
    slave

​

    

slave

这里就是我们用来测试的页面与前面一样设置了document.domain,并且尝试弹出第一个文件设置的cookie值 

将文件创建后以后,我们可以尝试使用访问一下slave.security.com测试:

跨域:利用iframe实现跨域DOM互访的四种方式_第3张图片

可以看到这里确实弹出了cookie,但是并不是文件1中设置的cookie,所以这种方法并没有实现真正的跨域互访DOM

方法2:

master.security.com 的index.html文件 :



​

    
    
    
    Master

​

    

master

     

这里和方法1一样设置了document.domain,但是不同的是,这里使用onload事件,必须要等到ifame加载完成后,在执行子页面的内容,然后这里还尝试将子页面的data尝试弹窗显示出来 

slave.security.com 的index.html文件:



​

    
    
    
    slave

​

    

slave

这里也是设置了与前面相同document.doamin,与方法1不同的是,这里没有了弹窗,反而定义了一全局变量 

然后我们就可以尝试访问matser.security.com测试:
跨域:利用iframe实现跨域DOM互访的四种方式_第4张图片

可以看到成功的弹窗了,并且弹出阿里data的值正是子页面定义的值,所以这里成功利用domain降域实现了跨域DOM互访

总:方法1在这个实验在浏览器中已经无法实现了,方法2可以正常实现

2、利用location.hash

(1)aaa.com(index.html文件)

   var ifr = document.createElement('iframe')
    ifr.src = 'http://www.bbb.com#data';
    ifr.style.display = 'none';
    document.body.appendChild(ifr);
    function checkHash() {
        try {
            let data = location.hash ? location.hash.substring(1) : ' ';
            console.log('获取到的数据为:', data);
        } catch (e) {
        }
    }
    checkHash();
    window.addEventListener('hashchange', function (e) {
        console.log('获取到的数据为:', location.hash.substring(1));
    });

这里首先将www.bbb.com子页面引入到本页面,并且增加一个锚点data,然后checkHash函数中进行了异常处理,try中对data进行判断,判断这是不是一个location.hash,如果是则取出#后的数据,不是就置为空,然后打印出data的数据 

后面再进行了对hashchange的监听

(2)bbb.com(index.html文件)

   switch (location.hash) { // 判断location1哈希
        case "#data":
            callback(); //调用函数
            break;
    }
    function callback() {
        const data = "some number:11111"
        try {
            parent.location.hash = data; //把自定义的值赋值给父页面的哈希值(即,改变了父页面的哈希)
        } catch (e) {
            //利用一个中间代理页面ifame(cs3.html)
            //要求:需要和第一个页面是同源
            var ifrproxy = document.createElement('iframe');
            ifrproxy.style.display = 'none';
            ifrproxy.src = "http://www.aaa.com/c.html#" + data;
            document.body.appendChild(ifrproxy);
        }
    }
    //获取父页面中的body标签中的内容

 这里对location.hash进行判断,如果是#data则调用回调函数,回调函数中对data进行了赋值,并且将该值赋值给了父级的location.hash,然后后面利用了c.html作为中间代理页面,将data值传给父级

(3)中间代理c.html文件

parent.parent.location.hash = self.location.hash.substring(1)

这里就是将自己的location.hash中后面#中的值,赋值给父级的父级也就是bbb.com的父级aaa.com 

(4)测试

我们尝试访问一下www.aaa.com

跨域:利用iframe实现跨域DOM互访的四种方式_第5张图片

可以看到我们成功的使用c.html作为中间代理,使用www.aaa.com访问到了www.bbb.com中的值,实现了跨域DOM互访的目的

缺点:

  • 数据直接暴露在URL中

  • 数据容量和类型都有限

总:这种方法已经被淘汰了

3、利用window.name

windows.name(一般在js代码中出现)的值不是一个普通的全局变量,而是当前窗口的名字,要注意的是每个iframe都有包裹它的window,而这个window是top window的子窗口,而它自然也有window.name的属性。

window.name的属性的神奇之处就在于name的值在不同的页面(甚至不同的域名)加载后依然存在(如果没有修改则值不会变化),并且可以支持非常长的name值(2MB)

举一个简单的例子:你在某个页面的控制台输入:

window.name="hello world"
window.location="http://www.baidu.com"

跨域:利用iframe实现跨域DOM互访的四种方式_第6张图片

页面跳转到了百度的页面,但是window.name却被保留下来,还是hello world

跨域:利用iframe实现跨域DOM互访的四种方式_第7张图片

那么我们现在可以利用window.name这一性质来尝试进行跨域访问

(1)security.com(window_name.html)

    var ifr = document.createElement('iframe');
    ifr.style.display = 'none';
    ifr.src = "http://www.aaa.com";
    document.body.appendChild(ifr);
    ifr.onload = function () {
        ifr.onload = function () {
            let data = ifr.contentWindow.name;
            console.info(data);
        }
        ifr.src = "http://www.security.com/c.html"
    }

这里的windo_name.html文件中使用iframe将www.aaa.com作为子页面引入到当前页面,然后使用加载时间,来获取当前页面的,window.name值,并且打印出来,后面还指定了监听源为:www.security.com/c.html 

(2)www.aaa.com(index.html)

    window.name = "hello aaaa";

这里只需要定义一个window.name即可 

(7)c.html

(8)测试

跨域:利用iframe实现跨域DOM互访的四种方式_第8张图片

可以看到这里成功的访问到了www.aaa.com中定义的window.name的值,并且,当前的Window.name的值为' '

总:当www.security.com/window_name.html在请求远端服务器www.aaa.com的数据时,我们可以在该页面下新建一个iframe,该iframe的src属性指向服务器地址(利用iframe标签的跨域能力),服务器文件aaa.com设置好了window.name值

但是由于window_name页面与aaa.com页面的iframe的src不同源的话,则无法操作iframe里面的任何东西,所以就取不到iframe的name值,所以我们只需要在aaa.com加载完成后重新换一个src指向同一个源的html文件,或者设置about:blank都行,这时候我们只要在window_name相同的目录下创建一个c.html空白文件即可,如果不重新指定src的话直接获取window.name就会报错

注:以上的方式均属于hack

4、利用postMessage(最推荐)

使用postmessage实现跨域访问

(1)security.com

  window.onload = function () {
        let targetOrigin = 'http://www.aaa.com'; //想要去的地址,即给它发送消息
        window.frames[0].postMessage('向aaa.com发送消息', targetOrigin);
    }
    window.addEventListener('message', function (e) {
        console.log('security.com接收到的消息', e.data);
    });

这里使用了一个事件监听着一个函数,函数中的tarOrigin指向www.aaa.com,这里表示为发送消息的接收地址,后面使用Postmessage向aaa.com发送消息,后面监听了消息这个事件 

(2)aaa.com

方案1:  
window.addEventListener('message', function (e) {
        if (e.source != window.parent)
            return;
        let data = e.data;
        console.log("aaa.com接收到的消息", data);
        // window.frames[0].postMessage("向aaa.com发消息", targetOrgin);
        parent.postMessage('aaa.com发送security', e.origin);
    }, false);

 这里定义了一个监听事件,监听消息,如果消息的源不是父级传递来的,则直接返回,否则将父级消息赋值给data,并且打印出来,然后后面也使用了postmessage向www.security发送消息

方案2:
    window.addEventListener('message', receiveMessage);
    function receiveMessage(event) {
        if (event.origin !== 'http://www.security.com') return;
        //事件的源地址
        if (event.data === 'Hello World') {
        //事件的内容
            event.source.postMessage('Hello', event.origin);
            
} else {
            console.log(event.data);
        }
    }

这里的实现是使用了event的几个属性来实现的 

(3)测试

方案1:

跨域:利用iframe实现跨域DOM互访的四种方式_第9张图片

可以看到,www.aaa.com和www.security.com都互相收到了跨域发送的消息

方案2:

跨域:利用iframe实现跨域DOM互访的四种方式_第10张图片

这里可以看到www.security也成功的接收到了ww.aaa发送的消息

使用postmessage读取其他窗口的localstorage(普通款)

(1)security/index.html

 window.onload = function () {
        let targetOrigin = 'http://www.aaa.com'; //想要去的地址,即给它发送消息
        var obj = { name: '杨攀帅', age: 20, 'wigth': 100 }
        window.frames[0].postMessage(
            JSON.stringify({ key: 'storage', method: 'set', data: obj }), targetOrigin
        );
    }
    window.addEventListener('message', function (e) {
        console.log('security.com接收到的消息', e.data);
    });

(2)aaa.com/index.html

    window.addEventListener('message', receiveMessage);
    function receiveMessage(event) {
        if (event.origin !== 'http://www.security.com') return; //信息来源判断
        var payload = JSON.parse(event.data);
        localStorage.setItem(payload.key, JSON.stringify(payload.data))
    }

(3)测试

跨域:利用iframe实现跨域DOM互访的四种方式_第11张图片

可以看到,aaa.com页面成功使用postmessage读取到了security.com页面中的数据,并且存储到了本地

使用postmessage读取其他窗口的localstorage(加强版本):

(1)security/index.html

 window.onload = function () {
        let targetOrigin = 'http://www.aaa.com'; //想要去的地址,即给它发送消息
        var obj = { name: 'yps', age: 20, 'wigth': 100 }
        //加强版
        window.frames[0].postMessage(
            JSON.stringify({ key: 'storage', method: 'set', data: obj }),
            'http://www.aaa.com'
        );
        window.frames[0].postMessage(
            JSON.stringify({ key: 'storage', method: 'get' }),
            '*'
        );
        window.frames[0].postMessage(
            JSON.stringify({ key: 'storage', method: 'remove' }),
            '*'
        );
    }
    window.onmessage = function (e) {
        if (e.origin != 'http://www.aaa.com') return;
        console.log(JSON.parse(e.data).name
        );
    };

这里的大致思路和上面一样,那为什么说它是一个加强版本呢,因为,这里我们可以自己选择时set、还是get、还是remove Messageage发送的消息 

需要注意的是,要想让数据存储下来,需要将remove注释掉,否则将无法正常的读取到数据

(2)aaa.com/index.html

window.addEventListener('message', receiveMessage);
    function receiveMessage(event) {
        if (event.origin !== "http://www.security.com") return;
        var payload = JSON.parse(event.data)
        switch (payload.method) {
            case 'set':
                localStorage.setItem(payload.key, JSON.stringify(payload.data))
                break;
            case 'get':
                var parent = window.parent;
                var data = localStorage.getItem(payload.key)
                parent.postMessage(data, 'http://www.security.com/')
                break;
             case 'remove':
                 localStorage.removeItem(payload.key)
                 break;
            default:
                break;
        }
    }

这里也是增加了set、get、remove的选项看,并且完成了对应操作的实现 

(3)测试

跨域:利用iframe实现跨域DOM互访的四种方式_第12张图片

这里也是一样的,可以看到,aaa.com页面成功使用postmessage读取到了security.com页面中的数据,并且存储到了本地

注:如果在将数据存入数据中没有转换/存储时转换为JSON格式时会报错

到这里使用iframe实现跨域DOM互访的几种方法已经介绍完毕了,更多跨域的内容请看下篇分享

你可能感兴趣的:(安全,前端,原型模式,前端,安全,html,开发语言,跨域,ifame)