整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          使用JavaScript實現跨標簽頁通信,建議收藏備用!

          Web 開發中,有時我們需要實現不同頁面之間的數據傳遞和事件觸發,比如一個頁面打開了另一個頁面,然后在新的頁面中操作后需要更新原來的頁面的內容。這種場景在電商、支付、社交等領域都很常見,那么如何用js來實現不同頁面之間的交互呢?本文提供幾種常見的方法供大家學習參考!

          一、localStorage

          在 Web Storage 中,每一次將一個值存儲到本地存儲時,都會觸發一個 storage 事件,通過 localStorage 結合 window.addEventListener('storage', cb) 完成 A、B 標簽頁間通信。

          // A標簽頁
          localStorage.setItem('send-msg', JSON.stringify({
              name: 'hzd',
              age: '18',
          }))
          
          // B標簽頁
          window.addEventListener('storage', (data) => {
              try {
                  console.log(data)
                  const msg = JSON.parse(data.newValue)
              } catch (err) {
                  // 處理錯誤
              }
          })
          


          在控制臺打印一下 data 的值,可以看到挺多信息:

          二、BroadcastChannel

          BroadcastChannel 通信方式的原理就是一個命名管道,它允許讓指定的同源下瀏覽器不同的窗口來訂閱它。

          每個 BroadcastChannel 對象都需要使用一個唯一的名稱來標識通道,這個名稱在同一域名下的不同頁面之間必須是唯一的,它允許同一域名下的不同頁面之間進行通信。

          通過 postMessage 方法,一個頁面可以將消息發送到頻道中,而其他頁面則可以監聽 message 事件來接收這些消息。通過這種方式是短線了一種實時通信的機制,可以在不同的頁面之間傳遞信息,實現頁面間的即時交流。如下圖所示:

          // A頁面
          const bc = new BroadcastChannel("test_channel");
          bc.postMessage("This is a test message.");
          
          
          // B頁面
          const bc = new BroadcastChannel("test_channel");
          bc.onmessage = (event) => {
            console.log(event);
          };
          


          三、postMessage

          postMessage 是 H5 引入的 API,該方法允許來自不同源的腳本采用異步方式進行有效的通信,可以實現跨文本文檔、多窗口、跨域消息傳遞,多用于窗口間數據通信,這也使它成為跨域通信的一種有效的解決方案。

          下面看兩個簡單的使用例子:

          示例一:

          // 發送端:
          
          <button id="btn">發送消息</button>
          
          <script>
            let device = window.open('http://localhost:63342/signal_communication/postMessage/receive.html')
          
            document.getElementById('btn').addEventListener('click', event => {
              device.postMessage('發送一條消息')
            })
          </script>
          


          // 接收端:
          
          <script>
            window.addEventListener('message', event => {
              console.log(event)
            })
          </script>
          


          示例二:

          // 發送端:
          
          <div>
              <input id="text" type="text" value="Runoob" />
              <button id="sendMessage" >發送消息</button>
          </div>
          <iframe id="receiver" src="https://c.runoob.com/runoobtest/postMessage_receiver.html" width="300" height="360">
              <p>你的瀏覽器不支持 iframe。</p>
          </iframe>
          <script>
          window.onload = function() {
              let receiver = document.getElementById('receiver').contentWindow;
              let btn = document.getElementById('sendMessage');
              btn.addEventListener('click', function (e) {
                  e.preventDefault();
                  let val = document.getElementById('text').value;
                  receiver.postMessage("Hello "+val+"!", "https://c.runoob.com");
              });
          }
          </script>
          


          // 接收端:
          
          <div id="recMessage">Hello World!</div>
          
          <script>
          window.onload = function() {
              let messageEle = document.getElementById('recMessage');
              window.addEventListener('message', function (e) {  // 監聽 message 事件
                  alert(e.origin);
                  if (e.origin !== "https://www.runoob.com") {  // 驗證消息來源地址
                      return;
                  }
                  messageEle.innerHTML = "從"+ e.origin +"收到消息: " + e.data;
              });
          }
          </script>
          


          四、SharedWorker

          SharedWorker 是一種在 Web 瀏覽器中使用的 Web API,它允許不同的瀏覽上下文,如不同的瀏覽器標簽頁之間共享數據和執行代碼。它可以用于在多個瀏覽上下文之間建立通信通道,以便它們可以共享信息和協同工作。

          與普通的 Worker 不同,SharedWorker 可以在多個瀏覽上下文中實例化,而不僅限于一個單獨的瀏覽器標簽頁或框架。這使得多個瀏覽上下文可以共享同一個后臺線程,從而更有效地共享數據和資源,而不必在每個標簽頁或框架中都創建一個獨立的工作線程。

          <!-- a.html -->
          <script>
            let index = 0;
            const worker = new SharedWorker("worker.js");
          
            setInterval(() => {
              worker.port.postMessage(`moment ${index++}`);
            }, 1000);
          </script>
          
          <!-- b.html -->
          <script>
            const worker = new SharedWorker("worker.js");
          
            worker.port.start();
            setInterval(() => {
              worker.port.postMessage("php是世界上最好的語言");
            }, 1000);
          
            worker.port.onmessage = function (e) {
              if (e.data) {
                console.log(e.data);
              }
            };
          </script>
          


          創建一個 worker.js 文件,并編寫以下代碼:

          let data = "";
          
          self.onconnect = (e) => {
            const port = e.ports[0];
          
            port.onmessage = function (e) {
              if (e.data === "php是世界上最好的語言") {
                port.postMessage(data);
                data = "";
              } else {
                data = e.data;
              }
            };
          };
          


          最終代碼運行效果如下圖所示:

          五、Service Worker

          Service Worker 它是一種服務工作線程,是一種在瀏覽器背后運行的腳本,用于處理網絡請求和緩存等任務。它是一種在瀏覽器與網絡之間的中間層,允許開發者攔截和控制頁面發出的網絡請求,以及管理緩存,從而實現離線訪問、性能優化和推送通知等功能。

          它在瀏覽器背后獨立運行與網頁分開,這意味著即使用戶關閉了網頁,Service Worker 仍然可以運行。可以用于實現推送通知功能。它可以注冊為推送消息的接收者,當服務器有新的通知要發送時,Service Worker 可以顯示通知給用戶,即使網頁沒有打開。

          要想使用,首先我們創建兩個不同的 html 文件分別代表不同的頁面,創建一個 Service Worker 文件,并且使用 live server 開啟一個本地服務器:

          <!-- a.html -->
          
          <!DOCTYPE html>
          <html lang="en">
            <head>
              <meta charset="UTF-8" />
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />
              <title>Document</title>
            </head>
            <body>
              <script>
                navigator.serviceWorker.register("worker.js").then(() => {
                  console.log("注冊成功");
                });
          
                setInterval(() => {
                  navigator.serviceWorker.controller.postMessage({
                    value: `moment ${new Date()}`,
                  });
                }, 3000);
          
                navigator.serviceWorker.onmessage = function (e) {
                  console.log(e.data.value);
                };
              </script>
            </body>
          </html>
          
          <!-- b.html -->
          
          <!DOCTYPE html>
          <html lang="en">
            <head>
              <meta charset="UTF-8" />
              <meta name="viewport" content="width=device-width, initial-scale=1.0" />
              <title>Document</title>
            </head>
            <body>
              <script>
                navigator.serviceWorker.register("worker.js").then(() => {
                  console.log("注冊成功");
                });
          
                setInterval(() => {
                  navigator.serviceWorker.controller.postMessage({
                    value: `moment ${new Date()}`,
                  });
                }, 3000);
          
                navigator.serviceWorker.onmessage = function (e) {
                  console.log(e.data.value);
                };
              </script>
            </body>
          </html>
          


          創建一個 worker.js 文件并編寫以下代碼:

          // worker.js
          self.addEventListener("message", function (e) {
            e.waitUntil(
              self.clients.matchAll().then(function (clients) {
                if (!clients || clients.length === 0) {
                  return;
                }
                clients.forEach(function (client) {
                  client.postMessage(e.data);
                });
              })
            );
          });
          


          最終代碼運行如下圖所示:

          你所編寫的 Service Worker 將遵守以下生命周期:

          1. 注冊: 在網頁的 JavaScript 代碼中調用 navigator.serviceWorker.register() 方法來注冊一個 Service Worker
          2. 安裝: 當 Service Worker 文件被下載并首次運行時,會觸發 install 事件。在 install 事件中,你可以緩存靜態資源,如 HTML、CSS、JavaScript 文件,以便在離線時使用
          3. 激活: 安裝成功后,Service Worker 并不會立即接管頁面的網絡請求。它需要等到之前的所有頁面都關閉,或者在下次頁面加載時才會激活()
          4. 就是因為編寫的 worker 代碼不生效,一直刷新都不生效,直到我關機重啟了才生效的......
          5. 控制: 一旦 Service Worker 被激活,它就開始控制在其作用域內的頁面。它可以攔截頁面發出的網絡請求,并根據緩存策略返回緩存的內容
          6. 更新: 當你更新 Service Worker 文件并再次注冊時,會觸發一個新的 install 事件。你可以在新的 install 事件中更新緩存,然后在下次頁面加載時進行激活,以確保新的 Service Worker 被使用
          7. 解除注冊: 如果你不再需要,可以通過調用 navigator.serviceWorker.unregister() 來解除注冊

          六、IndexDB

          IndexedDB 是一種在瀏覽器中用于存儲和管理大量結構化數據的 Web API。它提供了一種持久性存儲解決方案,允許 Web 應用程序在客戶端存儲數據,以便在不同會話、頁面加載或瀏覽器關閉之間保留數據。

          與傳統的 cookie 或 localStorage 等存儲方式不同,IndexedDB 更適合存儲復雜的、結構化的數據,例如對象、數組、鍵值對等。這使得它特別適用于應用程序需要存儲大量數據、執行高級查詢或支持離線工作的情況。

          要實現跨標簽通信,如下代碼所示:

          <!-- a.html -->
          <script>
            let index = 0;
            // 打開或創建 IndexedDB 數據庫
            const request = indexedDB.open("database", 1);
          
            request.onupgradeneeded = (event) => {
              const db = event.target.result;
              const objectStore = db.createObjectStore("dataStore", {
                keyPath: "key",
              });
            };
          
            request.onsuccess = (event) => {
              const db = event.target.result;
              const transaction = db.transaction(["dataStore"], "readwrite");
              const objectStore = transaction.objectStore("dataStore");
          
              // 存儲數據
          
              objectStore.put({ key: "supper", value: `moment` });
          
              transaction.oncomplete = () => {
                db.close();
              };
            };
          </script>
          
          <!-- b.html -->
          <script>
            // 打開相同的 IndexedDB 數據庫
            const request = indexedDB.open("database", 1);
          
            request.onsuccess = (event) => {
              const db = event.target.result;
              const transaction = db.transaction(["dataStore"], "readonly");
              const objectStore = transaction.objectStore("dataStore");
          
              // 獲取數據
              const getRequest = objectStore.get("supper");
          
              getRequest.onsuccess = (event) => {
                const data = event.target.result;
                if (data) {
                  console.log(data.value);
                }
              };
          
              transaction.oncomplete = () => {
                db.close();
              };
            };
          </script>
          


          最終代碼運行如下圖所示:

          七、cookie

          <!-- a.html -->
          <script>
            let index = 0;
            setInterval(() => {
              document.cookie = `supper=moment ${index++}`;
            }, 1000);
          </script>
          
          <!-- b.html -->
          <script>
            console.log("cookie 的值為: ", document.cookie);
          
            setInterval(() => {
              console.log("cookie 的值發生了變化: ", document.cookie);
            }, 1000);
          </script>
          


          具體代碼運行效果如下圖所示:


          作者:前端掘金者H
          鏈接:https://juejin.cn/post/7268602250653319202

          置知識

          什么是跨域?

          瀏覽器發送的請求地址(URL)與所在頁面的地址 不同(端口/協議/域名 其一不同)。簡言之,瀏覽器發出的請求url,與其所在頁面的url不一樣。此時,同源策略會讓瀏覽器拒收 服務器響應回來的數據,報錯信息如下:

          同源策略

          同源機制是瀏覽器處于安全考慮,只允許相同源的接口進行相互通信。不同源的接口在沒有得到對方授權的情況下不能夠讀寫對方的資源。這個很好理解,如果沒有同源機制,瀏覽器中的cookie就可以被隨意讀取,一旦用戶進入惡意網站,惡意網站就可以讀取用戶的cookie,偽造用戶進行登陸轉款等操作。

          同源策略的交互方式有三種:

          • 通常允許跨域寫操作,例如鏈接,重定向等。
          • 通常允許跨域嵌套資源,例如 img,script 標簽等。
          • 通常不允許跨域讀操作。

          同源指的的是同時滿足以下三個條件的地址:

          1. 同協議,如http或https
          2. 同域名,如www.baidu.com/login和www.baidu.com/addProduct是同域名,其中www.baidu.com是域名
          3. 同端口,如80端口等

          如:

          http://www.baidu.com
          https://www.baidu.com
          //這兩個網址由于協議不同,因此不屬于同源
          http://www.baidu.top
          http://www.baidu.com
          //這兩個網址由于域名不同,因此不屬于同源
          http://www.baidu.com:80
          http://www.baidu.com:8080
          //這兩個網址由于端口不同,因此不屬于同源
          https://www.baidu.com/login
          https://www.baidu.com/add
          //這兩個網址屬于同源
          

          最常用的四種跨域解決方案

          1.cors

          cors跨域資源共享允許是在服務端"Access-Control-Allow-Origin"字段設置的,當將cors設置為允許某個地址訪問時,該地址就可以跨域訪問這個服務器地址。當cors設置為"*"時即允許所有地址訪問時,則表示所有地址都可以跨域訪問這個服務器地址的資源。

          cors具體設置根據使用的后端語言不同會有所區別,此處以node的express框架設置為例:

          客戶端設置:

          <script>
           $.ajax({
           type: 'post',
           url: 'http://127.0.0.1:5000',
           success: function(res) {
           console.log('成功跨域');
           console.log(res);
           }
          })
          </script>
          

          node端設置:

          res.header("Access-Control-Allow-Origin", "*");//允許所有地址跨域訪問服務器資源
          

          2.jsonp

          我們在開始講同源策略的時候提到過,同源策略是允許script標簽跨域訪問資源的。jsonp方法就是利用的這個原理跨域的。

          以Jquery的ajax請求為例:

          <script>
          function jsonpCallback(data){
           console.log("成功跨域,數據為:",data) ;
          }
          $.ajax({
           method:"GET",
           url:"https://www.baidu.com",
           dataType:"jsonp",
           jsonpCallback:"jsonpCallback" //設置jsonp的回調函數
          })
          <script>
          

          優缺點:

          • 兼容性好,低版本的 IE 也支持這種方式。
          • 只能支持get方式的 HTTP 請求。

          3.window.postMessage

          postMessage跨域方法是HTML5的新特性,該方法必須接受兩個參數:

          1. message:需要傳遞的數據信息
          2. targetOrigin:需要發送請求的目標地址

          示例:

          發送端代碼:http://localhost:8081/send.html

          <!DOCTYPE html>
          <html lang="en">
          <head>
           <meta charset="UTF-8">
           <title>Title</title>
          </head>
          <body>
          <input type="button" onclick="post()" value="send">
          <script>
           var my_window=window.open("http://localhost:8082/index.html");
           function post() {
           var data={
           name:"martin",
           age:18
           };
           my_window.postMessage(JSON.stringify(data),"http://localhost:8082/index.html");
           }
          </script>
          </body>
          </html>
          

          點擊send按鈕,send端跨域postMessage發出一個字符串

          接收端代碼:http://localhost:8082/reciever.html

          <!DOCTYPE html>
          <html lang="en">
          <head>
           <meta charset="UTF-8">
           <title>Title</title>
          </head>
          <body>
          <input type="text">
          <script>
          window.addEventListener("message",function (message) {
           if(message.origin=="http://localhost:8081"){ //檢驗信息的來源,只要想要地址發送的信息
           var input=document.getElementsByTagName("input")[0];
           input.value=message.data; //將接收到的信息填入input框
           console.log(message);
           }
          })
          </script>
          </body>
          </html>
          

          此時觀察接受地址的input框,發現已經接受到了數據,至此跨域成功。

          備注:想要將幾個網頁在不同的端口打開,可以安裝http-server這個node模塊,無需配置,即裝即用。

          4.proxy(代理)

          原理:因為同源策略只是針對瀏覽器的安全策略,但是服務端并不受同源策略的限制,也就不存在跨域的問題。

          示例:

          當前狀況:

          • 瀏覽器前端域為http://localhost:3000,該頁面提供了接口可以向服務端http://localhost:3001發送請求

          需求:

          • 需要瀏覽器跨域向http://localhost:3002發送請求

          代理跨域:

          • 讓3000端口的前端先向3001端口的服務端發送請求,然后再讓服務端向目標地址3002端口發送請求
          • 然后服務端再將響應數據轉發給前端網頁

          總結:

          從這個例子的描述中可以看出,代理跨域方案說白了就是讓代理作為一個中間人,在前端和目標地址之間擔任一個信息轉發的功能,就有點像婆媳有矛盾不能直接溝通,所有一般讓丈夫做為中間人來傳話。

          總結

          • 協議,域名,端口號不全相同的資源之間相互通信時,就會產生跨域問題。
          • 出于安全考慮,瀏覽器的同源策略限制了不同域之間相互通信。
          • JSONP,CORS,Server Proxy都是常用的前后端跨域通信的解決方式,postMessage跨域解決方案主要是解決窗口頁面之間的數據通信。
          • JSONP 只支持 GET 方式的 HTTP 請求。
          • CORS 跨域方案是在后端進行設置的。
          • Server Proxy 就是讓服務器轉發自己的跨域請求,因為服務器不受到跨域策略限制。

          :本文為“小米安全中心”原創 ,轉載請聯系“小米安全中心”

          背景

          價值: 00

          漏洞原因:postMessage跨域漏洞導致,利用websocket接收用戶認證token

          原文地址:https://labs.detectify.com/2017/02/28/hacking-slack-using-postmessage-and-websocket-reconnect-to-steal-your-precious-token/

          基礎知識

          在了解這個漏洞的時候我們需要了解以下基礎的知識:PostMessage

          PostMessage

          PostMessage API是一個很簡單的實現跨域的方法,最基本的使用方法是:

          B站的data數據,發送數據端

          獲取數據代碼:

          PostMessage產生的漏洞

          偽造獲取數據端

          若是發送數據端的第二個參數使用的是"*" 那么表示的是這個數據可以被任何域獲取到,這時候只要構造window.addEventListener來接受數據就可以了

          偽造發送數據端

          來看一段代碼:

          可以看到對接收的message.origin進行了判斷,但是使用的正則并不標準(小米安全中心入駐安全脈搏賬號發布 轉載請注明來源安全脈搏https://www.secpulse.com/archives/56637.html)

          直接利用“wwwaexamplesender.com“, “wwwbexamplesender.com” etc.進行繞過,用postMessage發送數據給他接收

          使用的poc可以參考:

          測試代碼:window.postMessage("hello", "*")

          利用xss進行攻擊

          先看代碼:

          這個對來源進行了嚴格的限制,可以通過查找http://www.examplesender.com/上面的xss進行攻擊

          查看是否使用PostMessage

          這個可以使用chrome下的開發者工具,在source ——> Global Listeners 下面可以看到

          案例

          上面slack的漏洞是一個案例,總體的思路就是站點監聽著一個message,并且沒有判斷message的來源,導致可以給他發message,message中有websocket的url的話,站點會和發送message的站點建立websocket鏈接,兵器會把認證后的token傳遞給發送者站點

          該作者還有個PostMessage的漏洞,會影響到百萬網站,是AddThis這個插件導致的,不難理解。

          1.qq郵箱

          再國內的站點了翻了一個下午也沒有能找到一個可以利用的漏洞,不過在qq郵箱還是可以找到可以練習的地方

          首先打開mail.qq.com 使用開發者工具打開查看listeners

          我們構造測試代碼:

          window.postMessage("hello", "*")

          這時候對代下斷點進行調試,可以最終得到:

          window.postMessage('{"action":"resize","width":"222","height":400}', "*")

          方法有兩個一個是close,一個是resize。

          直接執行的話就可以看到登入框的大小屬性產生了變化,構造poc:

          構造html頁面,訪問這個頁面,就能看到登入框的大小產生了變化,

          這個只是個案例,要是郵箱中接收的信息做了其他處理,那么就有可能產生其他的問題

          2.微博

          qq郵箱是偽造發送者,那微博這個就是偽造接收者。同樣打開微博查看信息

          可以看到一行代碼:

          b

          && b

          .contentWindow && b

          .contentWindow.postMessage ? b

          .contentWindow.postMessage(g, "*")

          這個很明顯的使用了*的配置

          構造poc:

          訪問html頁面觸發:

          這個里面沒有任何可以利用的東西,但是一旦有用戶的敏感信息,那么就可以跨域獲取這些信息

          參考資料

          http://www.cnblogs.com/dolphinX/p/3464056.html

          https://labs.detectify.com/2016/12/15/postmessage-xss-on-a-million-sites/

          【注:本文為“小米安全中心”原創 小米安全中心入駐安全脈搏賬號發布 轉載請注明來源安全脈搏】

          【安識科技,是一家專注于賬號安全、企業風險評估的技術型企業。旗下擁有基于云+端的自研產品多因素令牌、基于插件的主被動多種掃描的企業級漏洞檢測云平臺。】

          【安全脈搏:分享技術、悅享品質。文章僅代表作者看法,如有不同觀點,歡迎添加安全脈搏微信號:SecPulse,進行交流。】


          主站蜘蛛池模板: 精品女同一区二区三区在线 | 中文乱码人妻系列一区二区| 亚洲视频一区网站| 在线视频一区二区三区四区| 亲子乱av一区二区三区| 最美女人体内射精一区二区| 精品一区二区三区在线观看l | 综合无码一区二区三区| 日韩精品人妻一区二区中文八零| 伊人久久大香线蕉AV一区二区| 风间由美性色一区二区三区| 亚洲国产成人久久综合一区77| 福利国产微拍广场一区视频在线 | 99精品国产一区二区三区2021 | 国产乱码精品一区三上| 国内自拍视频一区二区三区 | 国产精品丝袜一区二区三区| 国产精品一区二区三区99| 无码毛片一区二区三区中文字幕 | 亚洲av无码片区一区二区三区| 国产自产V一区二区三区C| 内射女校花一区二区三区| 久久久人妻精品无码一区| 一区二区三区在线| 爆乳熟妇一区二区三区霸乳| 美女AV一区二区三区| 性色AV 一区二区三区| 国产在线观看一区精品| 国产一区二区三区在线视頻 | 无码一区二区三区中文字幕| 日韩av片无码一区二区不卡电影| 蜜桃传媒一区二区亚洲AV| 无码一区二区三区免费视频| 日本一区二区三区日本免费 | 国产精品一区在线麻豆 | 无码人妻久久久一区二区三区| 亚洲国产成人一区二区三区| 国产精品揄拍一区二区久久| 国产一区二区三区播放心情潘金莲 | 在线欧美精品一区二区三区| 中文字幕日韩精品一区二区三区|