zoukankan      html  css  js  c++  java
  • window.postMessage实现网页间通信

    window.postMessage() 方法可以安全地实现跨域通信。通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为https),端口号(443为https的默认值),以及主机 (两个页面的模数 Document.domain设置为相同的值) 时,这两个脚本才能相互通信。window.postMessage() 方法提供了一种受控机制来规避此限制,只要正确的使用,这种方法就很安全。

    一、安装live-server

    要想实现跨窗口通信,必须要在服务器上运行,直接用浏览器打开HTML文件只能处理单个文件,窗口之间无法通信。

    npm install -g live-server
    

    使用命令live-server进行启动。

    安装live-server,在任意位置启动服务器。

    在服务器中启动之后,会看见文档中多了一个script标签。这段代码是由live-server插入的。当运行在后台的live-server检测到文件变化,就会通过websockt向网页发送“reload”消息,从而可以实现浏览器中的网页总是实时的响应文件的变更。

    	// <![CDATA[  <-- For SVG support
    	if ('WebSocket' in window) {
    		(function() {
    			function refreshCSS() {
    				var sheets = [].slice.call(document.getElementsByTagName("link"));
    				var head = document.getElementsByTagName("head")[0];
    				for (var i = 0; i < sheets.length; ++i) {
    					var elem = sheets[i];
    					head.removeChild(elem);
    					var rel = elem.rel;
    					if (elem.href && typeof rel != "string" || rel.length == 0 || rel.toLowerCase() == "stylesheet") {
    						var url = elem.href.replace(/(&|?)_cacheOverride=d+/, '');
    						elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf());
    					}
    					head.appendChild(elem);
    				}
    			}
    			var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://';
    			var address = protocol + window.location.host + window.location.pathname + '/ws';
    			var socket = new WebSocket(address);
    			socket.onmessage = function(msg) {
    				if (msg.data == 'reload') window.location.reload();
    				else if (msg.data == 'refreshcss') refreshCSS();
    			};
    			console.log('Live reload enabled.');
    		})();
    	}
    	// ]]>
    

    二、基础知识

    MessageEvent有以下几个属性:

    • data:从其他window中传递过来的对象
    • origin:调用 postMessage 时消息发送方窗口的 origin
    • source:对发送消息的窗口对象的引用; 您可以使用此来在具有不同origin的两个窗口之间建立双向通信。

    在发送数据窗口执行:otherWindow.postMessage(msg,origin)

    • otherWindow:表示接受数据的窗口的window对象,包括iframe的子窗口和通过window.open打开的新窗口。
    • msg表示要发送的数据,包扩字符串和对象(ie9以下不支持,可以利用字符串和json互换)。
    • origin表示接收的域名。

    三、最简单的一个demo

    父窗口打开一个子窗口,然后询问子窗口:“吃饭了吗”,子窗口回复父窗口:“吃了”
    father.html

    <html>
    
    <body>
    
    </body>
    <script>
        window.addEventListener("message", function(e) {
            document.querySelector("body").appendChild(document.createTextNode('son say: ' + e.data))
        })
        var son = window.open("son.html")
        son.onload = function() {//必须得要等到儿子加载完成才可以说话
            son.postMessage("吃饭了吗", location.href)
        }
    </script>
    
    </html>
    
    

    son.html

    <html>
    
    <body>
    
    </body>
    <script>
        window.addEventListener("message", function() {
            console.log(event)
            document.querySelector("body").appendChild(document.createTextNode("father say: " + event.data))
            event.source.postMessage("吃了", event.origin)
        })
    </script>
    
    </html>
    

    四、一个网页聊天系统

    father.html

    <html>
    
    <head>
        <style>
            textarea,
            input {
                 80%;
                font-size: 20px;
                font-family: "Consolas";
            }
            
            textarea {
                height: 80%;
            }
            
            input {
                height: 10%;
            }
        </style>
    </head>
    
    <body>
        <div style="text-align:center">
            <textarea readonly></textarea>
            <input type="text" style="margin-top:10px" onkeydown="keydown()">
        </div>
    </body>
    <script>
        function $(sel) {
            return document.querySelector(sel)
        }
        window.addEventListener("message", function() {
            $("textarea").value += "
    son say: " + event.data
        })
        var son = window.open("myson.html")
    
        function keydown() { //这里不需要传递参数,直接使用event就可以
            if (event.keyCode == 13) {
                son.postMessage($("input").value, location.href)
                $("textarea").value += "
    我说:" + $("input").value
                $("input").value = ""
                event.preventDefault
            }
        }
    </script>
    
    </html>
    

    myson.html

    <html>
    
    <head>
        <style>
            textarea,
            input {
                 80%;
                font-size: 20px;
                font-family: "Consolas";
            }
            
            textarea {
                height: 80%;
            }
            
            input {
                height: 10%;
            }
        </style>
    </head>
    
    <body>
    
        <div style="text-align:center">
            <textarea readonly></textarea>
            <input type="text" style="margin-top:10px" onkeydown="keydown()">
        </div>
    </body>
    <script>
        var father = null
        window.addEventListener("message", function() {
            $("textarea").value += "
    father say: " + event.data
            if (father == null) {
                father = {
                    source: event.source,
                    origin: event.origin
                }
            }
        })
    
        function $(sel) {
            return document.querySelector(sel)
        }
    
        function keydown() { //这里不需要传递参数,直接使用event就可以
            if (event.keyCode == 13) {
                father.source.postMessage($("input").value, location.href)
                $("textarea").value += "
    我说:" + $("input").value
                $("input").value = ""
                event.preventDefault
            }
        }
    </script>
    
    </html>
    

    五、最后一个demo

    <html>
    
    <head>
        <meta charset="UTF-8">
    </head>
    
    <body>
        <input type="button" value="Open Window" onclick="openWin()" />
    </body>
    <script>
        window.addEventListener("message", function(evt) {
            var ele = document.createElement("pre")
            ele.innerText = "son say:" + JSON.stringify(evt.data)
            document.querySelector("body").appendChild(ele)
        })
    
        var popupwin = window.open("son.html");
        //onload只能执行一次,也就是如果子窗口有onload事件,可能会覆盖。
        popupwin.onload = function(e) {
            var params = "天下大势为我所控"
            var origin = location.href
            popupwin.postMessage(params, origin);
        }
        popupwin.onunload = function(e) {
            var ele = document.createElement("h1")
            ele.innerText = "儿子最后说:" + popupwin.returnValue
            document.querySelector("body").appendChild(ele)
        }
    </script>
    
    </html>
    

    son.html

    <html>
    
    <head>
        <title>popup window</title>
    </head>
    
    <body>
        <button onclick="closeWin()">点我返回</button>
        <div id="show"></div>
    </body>
    <script>
        function closeWin() {
            window.returnValue = "这是返回值";
            window.close();
        }
        //HTML DOM fully loaded, and fired window.onload later. 
        document.onreadystatechange = function() {
            if (document.readyState === 'complete') {
                window.addEventListener('message', function(event) {
                    document.querySelector("#show").appendChild(document.createTextNode("father say:" + e.data))
                    event.source.postMessage("what's the fuck", event.origin)
                });
            }
        };
    </script>
    
    </html>
    

    六、安全问题

    如果您不希望从其他网站接收message,请不要为message事件添加任何事件侦听器。
    如果您确实希望从其他网站接收message,请始终使用origin和source属性验证发件人的身份。
    当您使用postMessage将数据发送到其他窗口时,始终指定精确的目标origin,而不是*。

    参考资料

    https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage

  • 相关阅读:
    logstash收集nginx日志写入kafka
    Logstash收集日志写入Redis
    Nginx反向代理kibana实现认证访问
    logstash收集nginx日志写入kafka1
    201999:渗透测试,基础学习,windows基础命令,笔记
    2019910:渗透测试,基础学习
    中华吸血鬼恶意病毒分析
    磁碟机病毒分析
    利用SQL语句对不同数据库进行高效果分页
    TimeBased Blind SQL Injection with Heavy Queries
  • 原文地址:https://www.cnblogs.com/weiyinfu/p/7764721.html
Copyright © 2011-2022 走看看