最近處理的案子需要在 A 網域登入後,同時在 B 網域一樣能保持登入的狀態。這就像登入 Google 一次後,旗下的各種服務例如 Gmail、Google Drive 等等,就算不同網域也不需要再重新登入。
實作的方式可以將登入資訊儲存在 HTML5 的 localStorage (就像以往的 cookie),在跨域的狀態下利用隱藏的 Iframe 來傳送請求,來取得另一網域的 localStorage。
以往跨域傳值很麻煩,而 HTML5 提供了 postMessage API 讓這件事變得簡單許多。本篇筆記將整理 postMessage 的以下操作:
(圖片出處: pixabay.com)
根據以上說明文件連結,postMessage 的使用方式大致如下:
瞭解原理後,以下來看實際範例。
上面的內嵌 IFRAME,為了示範傳值效果,因此沒有隱藏。在跨域傳值的實作上,這個 IFRAME 並不需要顯示出來,請用 CSS 將內嵌的 IFRAME 隱藏即可。
這個範例的作用如下:
讀者可自行輸入資料來測試 IFRAME 跨域傳值效果。
上面展示效果的母視窗程式碼,範例如下:
簡單說明如下:
上面展示效果的 IFRAME 程式碼,範例如下:
流程簡單說明如下:
一開始 postMessage API 只能傳送字串,這使得應用層面較狹隘且操作麻煩,例如要傳送陣列、物件前,得先執行 toString() 轉換成字串,然後接收到後再還原,真的多此一舉。
現在 postMessage 任何變數型態都能傳送了,例如物件、函數都可以,不要太舊的瀏覽器都可支援,以下是官網說明:
實作的方式可以將登入資訊儲存在 HTML5 的 localStorage (就像以往的 cookie),在跨域的狀態下利用隱藏的 Iframe 來傳送請求,來取得另一網域的 localStorage。
以往跨域傳值很麻煩,而 HTML5 提供了 postMessage API 讓這件事變得簡單許多。本篇筆記將整理 postMessage 的以下操作:
- 跨域傳值範例
- 如何確保安全性
- 傳送各種資料型態(包含物件、函數)
(圖片出處: pixabay.com)
一、postMessage 使用方法
根據以上說明文件連結,postMessage 的使用方式大致如下:
- 在頁面上開一個 Iframe 窗口,或是使用 window.open 開啟一個視窗
- 取得這個 Iframe 窗口或是新視窗的對象,方式如下:
- Iframe:var newFrame = window.frames["自訂名稱"]
- 新視窗:var newWindow = window.open("網址", 參數)
- 利用窗口對象發送 postMessage,例如 newWindow.postMessage(訊息內容, "目標網域")
- 在 Iframe 或新視窗之內,執行 addEventListener 監聽 message,可取得訊息內容
瞭解原理後,以下來看實際範例。
二、跨域傳值範例
上面的內嵌 IFRAME,為了示範傳值效果,因此沒有隱藏。在跨域傳值的實作上,這個 IFRAME 並不需要顯示出來,請用 CSS 將內嵌的 IFRAME 隱藏即可。
這個範例的作用如下:
- 本站母視窗網域為 www.wfublog.com,IFRAME 的網域為 demo.wfublog.com,是不同網域(網站 logo 有連結)
- 在母視窗輸入框填入任意字串後,會將字串傳送到 IFRAME 的網域,並顯示在該網頁上
- IFRAME 網頁並同時統計輸入的筆數,將數值存在該網頁的 js 變數
- IFRAME 網域每次接收到資料後,會統計資料筆數,並將數值回傳到母視窗網域
- 最後母視窗用 alert 彈跳訊息,顯示總輸入筆數
- 整個流程傳送、接收訊息時,為了維持安全性的因素,都會檢查發送的網域,不接收其他網域的訊息,以免被駭客攻擊
讀者可自行輸入資料來測試 IFRAME 跨域傳值效果。
三、母視窗程式碼範例
上面展示效果的母視窗程式碼,範例如下:
<!--載入 query-->
<script src='//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js'></script>
<!--跨域 IFRAME-->
<iframe name="WFU_frame" src="http://demo.wfublog.com/p/cross-domain-postmessage-iframe.html" onload="sendMessage()" style="width: 100%; height: 300px;"></iframe>
<!--Bootstrap 輸入框-->
<div class="input-group">
<input id="input_message" class="form-control input-lg" placeholder="請輸入傳輸字串" type="text">
<span class="input-group-btn">
<button id="submit_message" class="btn btn-primary btn-lg" style="margin-left:20px;">輸入</button>
</span>
</div>
<script>
//<![CDATA[
function sendMessage() {
// 送出字串
$("#submit_message").click(function() {
submit_message();
});
}
// 傳送訊息到 IFRAME 網域
function submit_message() {
// 發送訊息 要取得資料
window.frames["WFU_frame"].postMessage($("#input_message").val(), "http://demo.wfublog.com/p/cross-domain-postmessage-iframe.html");
$("#input_message").val("");
}
// 監聽 IFRAME 網域回傳的訊息
window.addEventListener("message", function(e) {
console.log(e.origin);
// 加強安全性 判斷數據發送方是否為可靠的網域; 不安全的網域不處理
if (e.origin.indexOf("demo.wfublog.com") < 0) {
return;
}
var data = e.data;
// 彈跳訊息框顯示回傳的筆數
alert(data);
});
</script>
簡單說明如下:
- js 使用了 jQuery,所以先載入 jQuery
- Iframe 實作上可用 css 隱藏起來
- Iframe 網頁載入後,自動執行 sendMessage 函數
- Bootstrap 輸入框的實作說明,可參考「Bootstrap 簡易版面實作範例﹍網格+面板+表單輸入+按鈕」
- submit_message 函數,會將輸入框的字串,傳送到 IFRAME 的指定網址
- 同時送出字串後,母視窗開始監聽 IFRAME 網域回傳的資料
- 如果回傳訊息非來自指定網域,則不處理
- 最後 alert 資料筆數
四、IFRAME 程式碼範例
上面展示效果的 IFRAME 程式碼,範例如下:
<script>
// 設定計數器, 起始值為 0
var count = 0;
// 監聽母視窗傳來的訊息
window.addEventListener('message', function(e) {
// 判斷數據發送方是否為可靠的地址
if (e.origin.indexOf("www.wfublog.com") < 0) {
return;
}
// 計數器 +1
count++;
// 回傳計數器數值
e.source.postMessage("已輸入 " + count + " 筆資料", e.origin);
});
</script>
流程簡單說明如下:
- 先設定計數器
- 開始監聽母視窗的訊息
- 如果傳來的訊息非來自指定網域,則不處理
- 每次收到資料,計數器 +1
- 最後回傳計數器的數值到母視窗
五、傳送的變數型態
一開始 postMessage API 只能傳送字串,這使得應用層面較狹隘且操作麻煩,例如要傳送陣列、物件前,得先執行 toString() 轉換成字串,然後接收到後再還原,真的多此一舉。
現在 postMessage 任何變數型態都能傳送了,例如物件、函數都可以,不要太舊的瀏覽器都可支援,以下是官網說明:
在 Gecko 6.0 之前 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3), message 参数必须是一个字符串。从 Gecko 6.0 (Firefox 6.0 / Thunderbird 6.0 / SeaMonkey 2.3) 开始,信息参数使用 结构化克隆算法 进行序列化。这意味着您可以将各种各样的数据对象安全地传递到目标窗口而无须自行序列化
更多 Javascript 相關技巧: