与跨域 iframe 通信示例
定义一个桥接类
class IframeBridge { constructor(iframe, targetOrigin = '*') { this.iframe = iframe; this.targetOrigin = targetOrigin; this.pendingRequests = new Map(); // 存储进行中的请求 this.setupMessageListener(); } setupMessageListener() { window.addEventListener('message', (event) => { // 安全检查:验证来源[1,3](@ref) if (this.targetOrigin !== '*' && event.origin !== this.targetOrigin) { return; } const message = event.data; // 检查是否为响应消息,并且有对应的请求ID if (message?._type === 'RESPONSE' && message?.requestId) { const pendingRequest = this.pendingRequests.get(message.requestId); if (pendingRequest) { // 从等待列表中移除 this.pendingRequests.delete(message.requestId); if (message.success) { // 成功则解析Promise pendingRequest.resolve(message.data); } else { // 失败则拒绝Promise pendingRequest.reject(new Error(message.error || 'Request failed')); } } } }); } async request(action, data = {}, timeout = 5000) { // 生成唯一请求ID[7](@ref) const requestId = this.generateRequestId(); return new Promise((resolve, reject) => { // 设置超时 const timeoutId = setTimeout(() => { this.pendingRequests.delete(requestId); reject(new Error('Request timeout')); }, timeout); // 存储resolve和reject以便后续使用 this.pendingRequests.set(requestId, { resolve: (responseData) => { clearTimeout(timeoutId); resolve(responseData); }, reject: (error) => { clearTimeout(timeoutId); reject(error); } }); // 向iframe发送消息[2,5](@ref) this.iframe.contentWindow.postMessage({ _type: 'REQUEST', requestId, action, data }, this.targetOrigin); }); } generateRequestId() { return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } }
Iframe 绑定事件
window.addEventListener('message', async (event) => {
const message = event.data;
// 处理请求类型的消息
if (message?._type === 'REQUEST') {
let responseData;
let success = true;
try {
// 根据不同的action执行相应操作
switch (message.action) {
case 'GET_USER_DATA':
//responseData = await getUserData(message.data.userId);
responseData = "I am user " + message.data.userId;
break;
case 'GET_USER_SETTINGS':
//responseData = await getUserSettings();
responseData = "I am UserSettingsxxxx";
break;
default:
throw new Error(`未知操作: ${message.action}`);
}
} catch (error) {
responseData = { error: error.message };
success = false;
}
// 回复消息,带回请求ID[3](@ref)
event.source.postMessage({
_type: 'RESPONSE',
requestId: message.requestId, // 必须带回原请求ID
success,
data: responseData
}, event.origin);
}
});
所有页面(包括 iframe)绑定事件(CS程序可以强制注入所有 html 页面)
window.addEventListener('load', function(){
var iframes = document.getElementsByTagName('iframe');
for (var i = 0; i < iframes.length; i++) {
var iframe = iframes[i];
var bridge = new IframeBridge(iframe);
iframe.bridge = bridge;
}
});
递归在所有嵌套 iframe 中执行代码返回多个结果,过滤出自己想要的结果
// 使用async/await进行通信 async function test() { var iframes = document.getElementsByTagName('iframe'); for (var i = 0; i < iframes.length; i++) { var iframe = iframes[i]; var bridge = iframe.bridge; if(!bridge) continue; try { console.log('请求用户数据...'); // 代码会在此"等待"直到收到响应 const userData = await bridge.request('GET_USER_DATA', { userId: 123 }); console.log('收到数据:', userData); // 继续执行后续逻辑 const settings = await bridge.request('GET_USER_SETTINGS'); console.log('用户设置:', settings); } catch (error) { console.error('通信失败:', error.message); } } }
桂棹兮兰桨,击空明兮溯流光。
浙公网安备 33010602011771号