一次诡异的登录失效

线上告警:用户上午 10:00 登录成功,10:15 刷新页面却跳回登录页。

排查发现,你给 token Cookie 设置了 Max-Age=900,但运维在 Nginx 又加了一层 proxy_cookie_path,把过期时间改写成 Session

用户浏览器里同时出现了两条同名 Cookie,一条 15 min 一条 Session,结果请求带的是后者------登录状态瞬间蒸发。

要彻底堵这种坑,得先拆清楚 Cookie 到底由哪些"零件"组成。


下面这段代码来自我们统一封装的 setCookie 工具,把每个字段都做成可选参数,避免手写字符串拼错。

ts 复制代码
// utils/cookie.ts
export const setCookie = (opts: {
  name: string;
  value: string;
  maxAge?: number;      // 秒
  expires?: Date;
  path?: string;
  domain?: string;
  secure?: boolean;
  sameSite?: 'Strict' | 'Lax' | 'None';
  httpOnly?: boolean;   // 🔥 仅服务端可写
}) => {
  const parts = [
    `${opts.name}=${encodeURIComponent(opts.value)}`,
    opts.maxAge   && `Max-Age=${opts.maxAge}`,
    opts.expires  && `Expires=${opts.expires.toUTCString()}`,
    opts.path     && `Path=${opts.path}`,
    opts.domain   && `Domain=${opts.domain}`,
    opts.secure   && 'Secure',
    opts.sameSite && `SameSite=${opts.sameSite}`,
    opts.httpOnly && 'HttpOnly',
  ].filter(Boolean);
  document.cookie = parts.join('; ');
};

🔍 关键决策点

  • encodeURIComponent 兜住中文或特殊符号。
  • Max-AgeExpires 二选一,现代浏览器优先 Max-Age
  • SameSite=None 必须同时带 Secure,否则 Chrome 直接拒绝。

原理剖析:从"键值对"到"安全策略"的三层视角

层级 字段 作用域 设计哲学
数据层 name=value 浏览器 ↔ 服务器 纯业务键值,必须 URL 编码
作用域层 Domain / Path 决定 Cookie 随哪些请求头走 最小权限原则,防跨域污染
安全层 Secure / HttpOnly / SameSite 决定谁能读、谁能写、何时发 纵深防御,层层收紧

时序文字图

  1. 浏览器收到 Set-Cookie: token=abc; Path=/api; Secure; SameSite=Lax
  2. 下次请求匹配 Path=/api 且为 HTTPS,才把 token=abc 塞进 Cookie: 头。
  3. 前端 JS 无法读取 HttpOnly,XSS 偷不走;SameSite=Lax 挡住 CSRF 的 POST 跨站。

生产环境我们拆成三档:

ts 复制代码
// 会话级:关闭标签即失效
setCookie({ name: 'ui-theme', value: 'dark', path: '/', sameSite: 'Lax' });

// 持久化:7 天免登录
setCookie({
  name: 'refreshToken',
  value: jwt,
  maxAge: 7 * 24 * 3600,
  path: '/auth',
  secure: true,
  sameSite: 'Strict',
});

// 服务端专用:前端不可见
setCookie({
  name: 'sessionId',
  value: uuid,
  httpOnly: true,
  secure: true,
  sameSite: 'None',
});

环境适配说明

  • 本地 localhost 开发时,Chrome 允许 Secure Cookie 通过 HTTP,但 Safari 不行,需加 start --ignore-certificate-errors 或直接用 HTTPS 自签证书。
  • 子域共享登录态时,Domain=.example.com 记得带点,否则只对当前子域生效。

举一反三:三个变体场景实现思路

  1. 多租户隔离

    Path 设成 /tenant/:id,前端切换租户时动态改写 Cookie,避免跨租户串号。

  2. A/B 实验灰度

    Max-Age=3600 的短期 Cookie 存分组 ID,服务端按 Cookie 值路由到不同版本,实验结束自然过期,无需清理脚本。

  3. 第三方埋点兼容

    埋点域名 analytics.xxx.com 需要回写 Cookie,但主站是 www.xxx.com,设置 Domain=xxx.com + SameSite=None; Secure,既跨子域又防 CSRF。


小结

  • Cookie 不是"一串字符串",而是 7 个独立旋钮的组合。
  • 把每个旋钮显式写成参数,就能在代码层面杜绝"拼错分号"这类低级 Bug。
  • 真正上线前,用 Chrome DevTools → Application → Cookies 面板再核对一次,比读 RFC 6265 更直观。
相关推荐
爱上妖精的尾巴4 分钟前
6-4 WPS JS宏 不重复随机取值应用
开发语言·前端·javascript
似水流年QC31 分钟前
深入探索 WebHID:Web 标准下的硬件交互实现
前端·交互·webhid
陪我去看海39 分钟前
测试 mcp
前端
speedoooo1 小时前
在现有App里嵌入一个AI协作者
前端·ui·小程序·前端框架·web app
全栈胖叔叔-瓜州2 小时前
关于llamasharp 大模型多轮对话,模型对话无法终止,或者输出角色标识User:,或者System等角色标识问题。
前端·人工智能
三七吃山漆2 小时前
攻防世界——wife_wife
前端·javascript·web安全·网络安全·ctf
用户47949283569152 小时前
面试官问"try-catch影响性能吗",我用数据打脸
前端·javascript·面试
GISer_Jing2 小时前
前端营销技术实战:数据+AI实战指南
前端·javascript·人工智能
GIS之路2 小时前
使用命令行工具 ogr2ogr 将 CSV 转换为 Shp 数据(二)
前端
嘉琪0013 小时前
Vue3+JS 高级前端面试题
开发语言·前端·javascript