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

子域名跨域Cookie隔离:客户端与管理端会话独立方案咨询

问题描述

我们的后端部署在https://api.example.com,另有两个客户端应用分别托管于https://client.example.com和https://admin.example.com,二者均调用同一后端接口。

我们计划通过HttpOnly Cookie实现会话管理,但需要确保client与admin的会话互不干扰——即同一浏览器的不同标签页可分别登录两个应用的不同账号。此外,我们还有结构相同的测试环境:后端位于https://api-test.example.com,应用分别在https://client-test.example.com和https://admin-test.example.com,需保证测试环境与生产环境的会话完全隔离。

当前后端基于Express实现的核心逻辑如下:

express
  .use((req, res, next) => {
    if (req.headers.origin && allowedOrigins.includes(req.headers.origin)) {
      res.setHeader("access-control-allow-credentials", "true")
      res.setHeader("access-control-allow-origin", req.headers.origin)
      res.setHeader("access-control-allow-methods", "GET,POST,PUT,DELETE,OPTIONS,HEAD")
      res.setHeader("access-control-allow-headers", ...)
    }
    next();
  })
  .post("/login", (req, res) => {
    //校验登录信息
    //...
    res.setHeader("set-cookie", `sessionid=${sessionid}; HttpOnly; SameSite=Strict;`);
    res.sendStatus(204);
  })

客户端通过fetch发起请求的示例如下:

fetch("https://api.example.com/ajax", {method: "POST", credentials: "include", ... })

目前基础功能正常:登录时后端会设置Domain为api.example.com、SameSite为Strict的Cookie,后续请求携带credentials: "include"时会自动带上该Cookie。

但当前存在问题:打开admin应用的标签页时,会与client应用共享同一个Cookie,导致admin请求后端时携带client的会话ID,且任一应用登录都会覆盖对方的会话。

我们尝试将Cookie的Domain属性设为请求的Origin,但浏览器开发者工具报错:

This Set-Cookie was blocked because its Domain attribute was invalid with regards to the current host url.

仅当Domain设为api.example.com或example.com时Cookie才能正常设置,但前者仍会导致client与admin共享会话,后者会使生产环境(api.example.com)与测试环境(api-test.example.com)的Cookie互相共享。

作为备选方案,我们考虑根据请求Origin命名Cookie,并在后端根据Origin校验对应的Cookie。但想咨询是否有其他Cookie属性或HTTP Header配置方式可实现需求?是否存在我们忽略的方案,或该需求本身无法实现?欢迎提供建议。


可行方案

1. 根据请求Origin区分Cookie名称

这是最直接且兼容所有浏览器的方案,完全匹配你的需求:

  • 登录时,后端解析请求的Origin头,根据来源生成对应名称的Cookie,比如给client.example.com生成sessionid_client,给admin.example.com生成sessionid_admin
  • Cookie的Domain仍设为api.example.com,确保客户端请求API时能携带对应Cookie
  • 后端处理请求时,根据Origin头选择对应的Cookie进行会话校验
  • 测试环境的Cookie会自动与生产环境隔离(因为Domain为api-test.example.com,和生产环境的api.example.com完全独立)

修改后的登录接口示例:

.post("/login", (req, res) => {
  //校验登录信息
  //...
  const origin = req.headers.origin;
  let cookieName = "sessionid";
  if (origin.includes("client.example.com")) {
    cookieName = "sessionid_client";
  } else if (origin.includes("admin.example.com")) {
    cookieName = "sessionid_admin";
  }
  // 测试环境可通过origin中的-test标识区分
  if (origin.includes("-test.example.com")) {
    cookieName += "_test";
  }
  res.setHeader("set-cookie", `${cookieName}=${sessionid}; HttpOnly; SameSite=Strict; Domain=api.example.com;`);
  res.sendStatus(204);
})

2. 使用Cookie的Partitioned属性(现代浏览器支持)

这是Chromium系浏览器(Chrome、Edge等)支持的新属性,用于实现跨站点Cookie分区存储:

  • 设置Cookie时添加Partitioned属性,同时必须指定SameSite=None和Secure(该属性仅针对跨站点场景生效)
  • 浏览器会为client.example.com和admin.example.com分别存储独立的sessionid Cookie,即使它们请求同一个API域名
  • 测试环境因Domain独立,无需额外处理

注意:该属性目前仅在Chromium系浏览器支持,Firefox和Safari尚未兼容,无法作为通用方案。

示例Cookie设置:

res.setHeader("set-cookie", `sessionid=${sessionid}; HttpOnly; SameSite=None; Secure; Partitioned; Domain=api.example.com;`);

3. 为客户端分配独立API子路径(可选)

如果允许调整API路由结构,可以通过路径隔离会话:

  • 给client分配https://api.example.com/client/*路径,admin分配https://api.example.com/admin/*路径
  • 设置Cookie时指定对应的Path属性:client的Cookie Path设为/client/,admin的设为/admin/
  • 客户端请求对应路径时才会携带对应Cookie,实现会话隔离

该方案需要调整API路由和客户端请求路径,改动较大,适合有重构计划的场景。


总结

最稳妥且兼容性最好的方案是根据Origin区分Cookie名称,可以完全满足会话隔离需求,且无需依赖浏览器新特性。Partitioned属性可作为未来的优化方向,但目前无法覆盖全量用户。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 01:20:16