You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现弹窗打开并等待关闭?Google OAuth2认证场景方案

实现可等待的OAuth2弹窗登录并检测关闭事件

核心实现思路

通过Promise封装弹窗操作,结合轮询检测弹窗的closed状态(跨域场景下最可靠的方式),同时处理弹窗拦截、加载错误等异常情况,最终实现await openPopup(url)的调用形式。

完整代码实现

1. 封装openPopup函数

function openPopup(url) {
  // 配置弹窗的大小与居中位置,提升用户体验
  const width = 600;
  const height = 600;
  const left = window.innerWidth / 2 - width / 2;
  const top = window.innerHeight / 2 - height / 2;
  
  // 打开弹窗,指定唯一窗口名称避免重复创建
  const popup = window.open(
    url,
    "GoogleAuthPopup",
    `width=${width},height=${height},left=${left},top=${top},popup=true,noopener,noreferrer`
  );

  // 处理弹窗被浏览器拦截的情况
  if (!popup) {
    return Promise.reject(new Error("弹窗被浏览器拦截,请开启弹窗权限后重试"));
  }

  return new Promise((resolve, reject) => {
    // 轮询检测弹窗是否关闭
    const checkInterval = setInterval(() => {
      if (popup.closed) {
        clearInterval(checkInterval);
        resolve();
      }
    }, 100);

    // 监听弹窗加载错误
    popup.addEventListener("error", () => {
      clearInterval(checkInterval);
      popup.close();
      reject(new Error("认证弹窗加载失败"));
    });
  });
}

2. 在登录函数中使用

async logIn() {
  try {
    const url = await this.getAuthUrl();
    await openPopup(url);
    // 弹窗关闭后执行后续流程,比如请求后端获取用户信息、更新登录状态
    const userData = await this.fetchAuthenticatedUser();
    // ... 其他业务逻辑
  } catch (err) {
    console.error("登录出错:", err);
    // 给用户展示错误提示,比如弹窗拦截、加载失败等
  }
}

进阶优化:区分「登录完成关闭」与「主动取消关闭」

如果后端处理完认证后,能跳转至一个同域回调页面,可以通过postMessage通知主页面登录状态,实现更精准的流程控制:

回调页面代码(同域)

// 通知主页面登录成功
window.opener.postMessage(
  { type: "GOOGLE_AUTH_SUCCESS" },
  window.opener.origin
);
// 关闭弹窗
window.close();

修改后的openPopup函数

function openPopup(url) {
  // ... 弹窗创建代码与拦截处理同之前 ...

  return new Promise((resolve, reject) => {
    let isAuthCompleted = false;
    const checkInterval = setInterval(() => {
      if (popup.closed) {
        clearInterval(checkInterval);
        window.removeEventListener("message", handleAuthMessage);
        resolve({ isCompleted: isAuthCompleted });
      }
    }, 100);

    // 监听来自弹窗的消息
    const handleAuthMessage = (event) => {
      // 验证消息来源,避免跨域恶意消息
      if (event.origin !== window.location.origin) return;
      if (event.data.type === "GOOGLE_AUTH_SUCCESS") {
        isAuthCompleted = true;
      }
    };
    window.addEventListener("message", handleAuthMessage);

    // ... 错误监听代码同之前 ...
  });
}

登录函数中区分处理

async logIn() {
  try {
    const url = await this.getAuthUrl();
    const { isCompleted } = await openPopup(url);
    if (isCompleted) {
      // 登录成功后的流程
      const userData = await this.fetchAuthenticatedUser();
    } else {
      // 用户主动取消登录的提示
      console.log("用户取消登录");
    }
  } catch (err) {
    // 错误处理
  }
}

关键注意事项

  • 跨域限制:主页面无法直接监听跨域弹窗的close事件,轮询是最兼容的方案。
  • 定时器清理:必须在弹窗关闭或出错时清除轮询定时器,避免内存泄漏。
  • 弹窗权限:提前提示用户开启弹窗权限,避免因拦截导致流程中断。

内容的提问来源于stack exchange,提问作者Samuele B.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 08:13:12