解决跨域 Cookie

发布: (2026年2月17日 GMT+8 02:15)
4 分钟阅读
原文: Dev.to

Source: Dev.to

Cover image for Solving Cross-Origin Cookies

问题

  • 前端部署在 Vercel(不同域名)
  • 后端(REST API 与 WebSocket)部署在 Render

由于域名不同,浏览器在 HTTP 请求中不会携带 Cookie。应用依赖 HttpOnly Cookie 来进行 REST API 认证和 WebSocket 授权。没有 Cookie,这两种机制都会失效。

将 Cookie 的 sameSite 属性改为允许跨域发送即可。

// From:
sameSite: 'strict'
// To:
sameSite: 'none'

为了保持 Cookie 的安全性,还启用了 secure 标记(仅在 HTTPS 下发送)。这解决了 REST API 的认证问题,但仍有阻止第三方 Cookie 的浏览器会阻止 Cookie 发送,导致 WebSocket 连接同样失效。

将前端设为代理(同源)

第一个变通办法是通过 Vercel 前端代理 API 调用,使请求在浏览器看来是同源的。

// Before proxy
API_URL="https://message-app.render.com/:path"

// After proxy
API_URL="https://message-app.vercel.com/:path"

有了此更改,浏览器不再把 REST 请求视为跨域,认证得以成功。

对于 WebSocket 客户端,在握手时加入 withCredentials 选项以携带 Cookie:

const socket = io(URL, { withCredentials: true });

即使跨域环境配置正确,许多浏览器 在 WebSocket 握手期间也不会发送 Cookie。在此设置中:

  • REST API 请求通过 Vercel 的代理(同源)。
  • WebSocket 升级直接连接到 Render(跨域)。

因为握手是跨域的,浏览器会阻止 HttpOnly Cookie,导致认证失败。

将令牌存于内存并使用 Socket.io Auth

解决方案是不要在 WebSocket 连接中依赖 Cookie,而是:

  1. 从已认证的端点获取短期令牌(该端点会验证 HttpOnly Cookie)。
  2. 将令牌存入内存(React context、Redux 等)。
  3. 在握手时通过 Socket.io 的 auth 参数传递令牌
// Fetch token from authenticated endpoint, then pass explicitly
const socket = io(URL, {
  auth: { token: authToken }
});

双重认证策略

  • REST API:继续使用 HttpOnly Cookie(例如 5 天有效期)。
  • WebSocket:使用单独的、短期的令牌(例如 1 小时),仅保存在内存中。

如果攻击者通过 XSS 获取了 WebSocket 令牌,泄露范围仅限于令牌的短暂有效期,并且在页面刷新后即失效。

要点

  • 当前端和后端位于不同域名时,跨域 Cookie 需要 SameSite: 'none'Secure: true
  • 即使 CORS 设置正确,浏览器仍可能在 WebSocket 握手时阻止 Cookie
  • 通过前端代理 API 调用可以解决 REST 的同源问题,但对 WebSocket 无效。
  • 基于令牌的 WebSocket 认证(存于内存) 提供了可靠且安全的变通方案。

如果你在多个域名之间部署实时应用,预计需要实现类似的双重认证模式。

0 浏览
Back to Blog

相关文章

阅读更多 »