You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

父窗口捕获登录弹窗关闭事件的替代方案咨询(覆盖手动与程序化关闭场景)

父窗口捕获登录弹窗关闭事件的替代方案咨询(覆盖手动与程序化关闭场景)

你当前通过postMessage处理程序化关闭弹窗的逻辑是可行的,但确实没覆盖用户手动关闭弹窗的场景。下面给你补充两种靠谱的方案,能同时覆盖手动关闭和程序化关闭的情况,你可以根据需求选择结合使用:

方案一:父窗口定时检查弹窗的closed属性

这是最直接的兜底方案——父窗口每隔一段时间检查弹窗实例的closed属性,一旦为true,就判定弹窗已经关闭(不管是手动点击关闭还是代码调用window.close())。

父窗口代码修改:

在创建authWindow并添加message监听器后,新增定时器检查逻辑:

const width = 600;
const height = 700;
const left = window.screenX + (window.outerWidth - width) / 2;
const top = window.screenY + (window.outerHeight - height) / 2;
const url = `${window.location.origin}/auth-onedrive`;
const authWindow = window.open(
  url, "OneDrive Login", `width=${width},height=${height},left=${left},top=${top}`
);

if (!authWindow) {
  console.warn("Popup blocked");
  retryLogin();
  return;
}

// 新增:定时检查弹窗状态
let popupCheckInterval = null;
popupCheckInterval = setInterval(() => {
  if (authWindow.closed) {
    console.log("弹窗已关闭(手动或程序化)");
    // 清理资源,避免内存泄漏
    window.removeEventListener("message", handleMessage);
    clearInterval(popupCheckInterval);
    // 处理关闭后的逻辑,比如提示用户登录中断
    handlePopupUnexpectedClose();
  }
}, 500); // 每隔500ms检查一次,可根据需求调整频率

const handleMessage = (event) => {
  // 🔴 重点补充:先验证消息来源的origin,防止跨站攻击
  if (event.origin !== window.location.origin) return;
  
  if (event.data?.type === "onedrive-auth-result") {
    console.log("Received message from popup:", event.data.status);
    // 清理资源
    window.removeEventListener("message", handleMessage);
    clearInterval(popupCheckInterval);
    retryLogin();
  }
};

window.addEventListener("message", handleMessage);

// 自定义弹窗意外关闭的处理逻辑
function handlePopupUnexpectedClose() {
  console.warn("用户未完成登录就关闭了弹窗");
  // 比如提示用户或提供重试选项
}

方案二:弹窗页面通过beforeunload事件主动通知父窗口

不管是手动点击关闭按钮,还是代码调用window.close(),弹窗的beforeunload事件都会触发。我们可以利用这个事件,在弹窗即将关闭时统一通知父窗口。

弹窗页面代码修改:

在弹窗页面添加beforeunload监听器,同时优化现有通知逻辑:

dispatchRedux(oneDriveLogin(payload, "Authenticating with OneDrive..."))
  .then(handleResponse)
  .catch(handleError);

const notifyParent = (status) => {
  if (window.opener) {
    // 🔴 补充指定目标origin,提升安全性
    window.opener.postMessage(
      { type: "onedrive-auth-result", status },
      window.opener.location.origin
    );
  }
};

// 新增:监听弹窗即将卸载的事件
window.addEventListener('beforeunload', () => {
  // 通知父窗口弹窗即将关闭
  notifyParent("closed");
});

const handleResponse = (res) => {
  if (res?.status === 'success' && res?.data?.loggedIn) {
    setLoginStatus("Login Successful.");
    notifyParent("success");
    window.close();
  } else if (res?.status === 'success' && !res?.data?.loggedIn) {
    setLoginStatus("Redirecting...");
    window.location.href = res?.data?.redirectUrl;
  }
};

父窗口对应修改handleMessage

需要区分“登录成功后关闭”和“未完成登录就关闭”的情况:

let isAuthCompleted = false; // 标记是否已完成登录

const handleMessage = (event) => {
  if (event.origin !== window.location.origin) return;
  
  if (event.data?.type === "onedrive-auth-result") {
    console.log("Received message from popup:", event.data.status);
    
    window.removeEventListener("message", handleMessage);
    clearInterval(popupCheckInterval); // 如果同时用了方案一,记得清理定时器
    
    if (event.data.status === "success") {
      isAuthCompleted = true;
      retryLogin();
    } else if (event.data.status === "closed") {
      if (!isAuthCompleted) {
        // 处理未完成登录就关闭的情况
        handlePopupUnexpectedClose();
      }
    }
  }
};

关键注意事项

  1. 必须验证消息来源:所有postMessage的处理逻辑都要先检查event.origin,防止恶意网站发送伪造消息,避免XSS攻击风险。
  2. 及时清理资源:不管是收到消息还是检测到弹窗关闭,都要及时移除事件监听器、清理定时器,避免内存泄漏。
  3. 平衡检查频率:方案一的定时器频率不要设置得过高(比如不要低于100ms),否则会增加不必要的性能开销。

你可以把这两个方案结合使用,比如用方案二做主动通知,方案一做兜底检查,这样能最大程度保证覆盖所有关闭场景~

火山引擎 最新活动