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

跨域SSO认证系统安全传参方案咨询(Node.js+React)

跨域SSO无Cookie/无关联ID数据库的Token传递方案

针对你遇到的URL传Token留痕的安全问题,结合Node.js+Express和React技术栈,给你两个实用的解决方案:

1. 基于PostMessage+隐藏iframe的前端安全传递

这个方案完全规避了URL传参的风险,Token仅在前端页面间通过内存传递,不会被浏览器历史、服务器日志记录。

实现步骤:

  • 认证域(auth.aettr.pt):用户登录成功后,创建隐藏iframe指向应用域的专门接收页面,通过postMessage把Token发送给iframe
  • 应用域(my.aettr.pt):接收页面监听message事件,验证消息来源后存储Token,再通知认证域关闭iframe并跳转至应用主页面

认证域React代码示例:

// 登录成功后的处理逻辑
const handleLoginSuccess = (token) => {
  const iframe = document.createElement('iframe');
  iframe.style.display = 'none';
  iframe.src = 'https://my.aettr.pt/sso-receiver';

  iframe.onload = () => {
    // 严格指定目标域名,防止恶意页面拦截
    iframe.contentWindow.postMessage(
      { type: 'SSO_AUTH_TOKEN', token },
      'https://my.aettr.pt'
    );
  };

  // 监听应用域的接收确认
  window.addEventListener('message', (event) => {
    if (event.origin === 'https://my.aettr.pt' && event.data.type === 'TOKEN_RECEIVED') {
      document.body.removeChild(iframe);
      // 跳转到应用主页
      window.location.href = 'https://my.aettr.pt/dashboard';
    }
  }, { once: true });

  document.body.appendChild(iframe);
};

应用域接收页面(React组件):

// my.aettr.pt/sso-receiver
import { useEffect } from 'react';

const SSOReceiver = () => {
  useEffect(() => {
    const handleMessage = (event) => {
      // 必须验证来源,防止跨域攻击
      if (event.origin !== 'https://auth.aettr.pt') return;

      if (event.data.type === 'SSO_AUTH_TOKEN' && event.data.token) {
        // 用sessionStorage存储Token,仅当前会话有效
        sessionStorage.setItem('auth_token', event.data.token);
        // 通知认证域已接收完成
        event.source.postMessage({ type: 'TOKEN_RECEIVED' }, event.origin);
      }
    };

    window.addEventListener('message', handleMessage);
    return () => window.removeEventListener('message', handleMessage);
  }, []);

  return <div style={{ display: 'none' }}></div>;
};

export default SSOReceiver;

应用域Express配置:

需要允许认证域嵌入接收页面,设置CSP头:

app.use((req, res, next) => {
  if (req.path === '/sso-receiver') {
    // 只允许auth.aettr.pt嵌入这个页面
    res.setHeader('Content-Security-Policy', 'frame-ancestors https://auth.aettr.pt');
  }
  next();
});

2. 加密URL片段(Fragment)传递

如果觉得iframe方案稍复杂,可以用URL片段(#后面的内容)传递——片段不会被发送到服务器,也不会出现在后端日志里,仅存在浏览器本地历史。配合加密进一步降低风险。

实现步骤:

  • 认证域用对称加密(比如AES)加密Token,把加密后的内容放到URL片段里重定向到应用域
  • 应用域前端读取片段内容,解密得到Token后,用history.replaceState清空片段,避免留在地址栏

认证域Node.js加密代码:

const crypto = require('crypto');

// 预先约定的AES密钥(32字节,对应AES-256)和IV长度
const ENCRYPTION_KEY = Buffer.from('your_32_byte_secure_secret_key', 'utf8');
const IV_LENGTH = 16;

function encryptToken(token) {
  const iv = crypto.randomBytes(IV_LENGTH);
  const cipher = crypto.createCipheriv('aes-256-cbc', ENCRYPTION_KEY, iv);
  let encrypted = cipher.update(token);
  encrypted = Buffer.concat([encrypted, cipher.final()]);
  // 返回IV+加密内容,用冒号分隔
  return `${iv.toString('hex')}:${encrypted.toString('hex')}`;
}

// 登录成功后的重定向逻辑
app.get('/auth-success', (req, res) => {
  const authToken = 'your_generated_jwt_or_token';
  const encryptedToken = encryptToken(authToken);
  res.redirect(`https://my.aettr.pt/#encrypted_token=${encryptedToken}`);
});

应用域React解密代码:

import { useEffect } from 'react';
const crypto = require('crypto');

const ENCRYPTION_KEY = Buffer.from('your_32_byte_secure_secret_key', 'utf8');

function decryptToken(encryptedText) {
  const [ivHex, encryptedHex] = encryptedText.split(':');
  const iv = Buffer.from(ivHex, 'hex');
  const encryptedBuffer = Buffer.from(encryptedHex, 'hex');
  const decipher = crypto.createDecipheriv('aes-256-cbc', ENCRYPTION_KEY, iv);
  let decrypted = decipher.update(encryptedBuffer);
  decrypted = Buffer.concat([decrypted, decipher.final()]);
  return decrypted.toString();
}

const App = () => {
  useEffect(() => {
    const hashParams = new URLSearchParams(window.location.hash.slice(1));
    const encryptedToken = hashParams.get('encrypted_token');

    if (encryptedToken) {
      try {
        const token = decryptToken(encryptedToken);
        sessionStorage.setItem('auth_token', token);
        // 清除地址栏的片段,避免Token泄露
        history.replaceState({}, document.title, window.location.pathname);
      } catch (err) {
        console.error('Token解密失败', err);
        // 解密失败则跳回认证页
        window.location.href = 'https://auth.aettr.pt/login';
      }
    }
  }, []);

  return <div>应用主内容</div>;
};

export default App;

方案对比

  • PostMessage+iframe方案:安全性最高,Token完全不经过URL,适合对安全要求高的场景
  • 加密URL片段方案:实现更简单,适合快速迭代的场景,但浏览器本地历史仍会保留加密后的内容,风险略高

内容的提问来源于stack exchange,提问作者Tomás Figueiredo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 21:35:19