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

求助:跨域场景下登录弹窗与父页面的Cross Window Communication问题

跨域登录弹窗通信与全站请求头设置解决方案

核心问题拆解

你遇到的是跨域场景下弹窗与父页面的通信障碍,以及登录后全站请求头的统一注入需求,以下是几种可行的解决方案:

方案1:使用postMessage实现跨域通信(推荐)

这是浏览器原生支持的跨域通信API,只要两端配合验证来源,就能安全传递数据。

父页面(主站)代码

// 打开登录弹窗并保存实例
const loginPopup = window.open('https://your-auth-domain.com/login', 'loginWindow', 'width=500,height=600');

// 监听弹窗发来的消息
window.addEventListener('message', (event) => {
  // 严格验证消息来源,防止恶意攻击
  if (event.origin !== 'https://your-auth-domain.com') return;

  // 处理登录成功的消息
  if (event.data.type === 'LOGIN_SUCCESS') {
    const { userInfo, authToken } = event.data;

    // 关闭弹窗
    loginPopup.close();

    // 全站注入请求头(以axios为例)
    axios.interceptors.request.use(config => {
      config.headers['Authorization'] = `Bearer ${authToken}`;
      config.headers['X-User-ID'] = userInfo.id;
      // 按需添加其他用户信息头
      return config;
    });

    // 可选:刷新页面或更新页面登录状态
    window.location.reload();
  }
});

弹窗页面(认证域)代码

// 凭证验证成功后执行
async function handleLoginSuccess() {
  const response = await fetch('https://your-auth-domain.com/api/verify', {
    method: 'POST',
    body: JSON.stringify(credential),
    headers: { 'Content-Type': 'application/json' }
  });

  if (response.ok) {
    const { userInfo, authToken } = await response.json();

    // 向父页面发送消息,指定目标域名限制接收范围
    window.opener.postMessage(
      { type: 'LOGIN_SUCCESS', userInfo, authToken },
      'https://your-main-domain.com'
    );

    // 延迟关闭弹窗,确保消息发送完成
    setTimeout(() => window.close(), 100);
  }
}

方案2:服务器端中转(兼容旧场景)

如果无法使用postMessage,可以通过服务器重定向到父域的回调页面来传递数据(注意敏感信息必须加密)。

步骤1:认证服务器重定向

凭证验证成功后,服务器返回重定向响应:

HTTP/1.1 302 Found
Location: https://your-main-domain.com/login-callback?token=ENCRYPTED_TOKEN&user=ENCRYPTED_USER_INFO

步骤2:父域回调页面代码(login-callback.html)

window.addEventListener('load', () => {
  // 解析URL参数(注意解密)
  const params = new URLSearchParams(window.location.search);
  const authToken = decrypt(params.get('token'));
  const userInfo = JSON.parse(decrypt(params.get('user')));

  // 调用父页面提前定义的方法
  window.opener.setLoginCredentials(authToken, userInfo);

  // 关闭回调页面
  window.close();
});

步骤3:父页面提前定义处理方法

function setLoginCredentials(authToken, userInfo) {
  // 存储凭证到localStorage,方便后续请求调用
  localStorage.setItem('authToken', authToken);
  localStorage.setItem('userInfo', JSON.stringify(userInfo));

  // 全站注入请求头
  axios.interceptors.request.use(config => {
    config.headers['Authorization'] = `Bearer ${authToken}`;
    config.headers['X-User-ID'] = userInfo.id;
    return config;
  });

  // 更新页面状态
  window.location.reload();
}

方案3:同根域子域名专属方案

如果主站和认证站是同根域(如a.example.com和b.example.com),可以通过统一document.domain+localStorage+storage事件实现通信:

弹窗页面代码

document.domain = 'example.com';
localStorage.setItem('loginInfo', JSON.stringify({ userInfo, authToken }));
window.close();

父页面代码

document.domain = 'example.com';
window.addEventListener('storage', (event) => {
  if (event.key === 'loginInfo') {
    const { userInfo, authToken } = JSON.parse(event.newValue);
    // 注入请求头...
    // 清理localStorage避免残留
    localStorage.removeItem('loginInfo');
  }
});

注意:此方法仅适合同根域子域名,跨顶级域会被浏览器安全策略阻止。

全站请求头设置通用方法

无论用哪种通信方式,拿到凭证后都可以通过封装请求工具来统一注入头:

  • Axios:使用请求拦截器(如上述示例)
  • 原生Fetch:封装工具函数
function authenticatedFetch(url, options = {}) {
  const authToken = localStorage.getItem('authToken');
  const userInfo = JSON.parse(localStorage.getItem('userInfo'));

  options.headers = {
    ...options.headers,
    'Authorization': `Bearer ${authToken}`,
    'X-User-ID': userInfo?.id
  };

  return fetch(url, options);
}

之后全站使用authenticatedFetch代替原生fetch即可。

内容的提问来源于stack exchange,提问作者SoJS

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 23:50:26