Playwright 登录态莫名失效?一次 Session Cookie 丢失的排查记录

举报
yd_282916250 发表于 2026/11/05 17:26:05 2026/11/05
【摘要】 自动化脚本昨天还好好的,今天登录态突然失效,但 Cookie 明明都还在且有效期到 2027 年。本文记录完整排查过程:区分持久 Cookie 与 Session Cookie、定位强杀进程导致的丢失链路,并给出优雅关闭、CDP 附加复用、自动重登三层对策。

现象:昨天还好好的,今天登录态没了

自动化脚本最让人头疼的一类问题:代码一行没改,昨天跑得好好的,今天突然登录失效了。

第一反应通常是"Cookie 过期了"。但打开开发者工具一看,登录态相关的 Cookie 明明都在:

cbc-sid    .huaweicloud.com    到期时间 2027-10-31
vk         .huaweicloud.com    到期时间 2027-10-31
ua         .huaweicloud.com    到期时间 2027-10-31

有效期还有一年多,看起来完全正常。可页面就是显示未登录,访问需要登录的页面会被重定向到登录页。

这就是本文要拆解的问题:Cookie 在,为什么登录态还是丢了?


第一步:区分"持久 Cookie"和"Session Cookie"

这是整个排查的关键认知。浏览器里的 Cookie 分两类:

类型 特征 存储位置
持久 Cookie 有明确的 Expires / Max-Age 写入磁盘,浏览器重启后仍在
Session Cookie 无过期时间(expires = -1) 通常只在内存中,浏览器关闭即失效

很多人(包括我)看到 cbc-sid 有效期到 2027 年,就以为登录态稳如泰山。但实际上,真正维持会话的往往是一批 Session Cookie。

用脚本把 Cookie 分类导出来看:

const cookies = await ctx.cookies();
cookies.forEach((c) => {
  const flag = c.expires > 0
    ? 'PERSIST  ' + new Date(c.expires * 1000).toISOString()
    : 'SESSION  (关闭即失效)';
  console.log(`${flag}  ${c.name}  @ ${c.domain}`);
});

输出结果一目了然:

PERSIST   2027-10-31  cbc-sid        @ .huaweicloud.com
PERSIST   2027-10-31  vk             @ .huaweicloud.com
SESSION   (关闭即失效)  SSOJTC         @ .auth.huaweicloud.com
SESSION   (关闭即失效)  CASTGC         @ .id1.cloud.huawei.com
SESSION   (关闭即失效)  SessionID      @ .huaweicloud.com
SESSION   (关闭即失效)  SSOTGC         @ .auth.huaweicloud.com

cbc-sid 这类持久 Cookie 更像"设备标识",而真正的登录凭证是 SSOJTC / CASTGC / SessionID 这批 Session Cookie。

它们的有效期不是由 Expires 决定的,而是由服务端 session 的存活状态决定。客户端这边一旦丢失,就再也拿不回来了。


第二步:找到 Session Cookie 的丢失时机

既然是"关闭即失效",那问题就聚焦到:浏览器是怎么被关掉的?

翻回自己的自动化代码,答案很清楚了——为了图省事,清理进程时用的是强制杀:

# 危险操作
Get-CimInstance Win32_Process -Filter "Name='chrome.exe'" |
  Where-Object { $_.CommandLine -like "*ms-playwright*" } |
  ForEach-Object { Stop-Process -Id $_.ProcessId -Force }

Stop-Process -Force 相当于直接 kill -9,浏览器没有机会走正常的退出流程:

  • 不会把内存中的 Session Cookie 刷写到磁盘
  • 不会清理和保存会话状态
  • 下次启动就是一个"干净"的浏览器

Playwright 自己的 ctx.close() 则不同,它会走完整的关闭流程,Session Cookie 有机会被保留下来(Chromium 支持"恢复上次会话"时会持久化这部分数据)。

所以:每强杀一次,就丢一次登录态。


第三步:三条对策

对策一:优雅关闭,别强杀

// 正确
await ctx.close();

// 或者封装一层,附加模式下不要关掉别人的浏览器
async function closeQuiet(ctx) {
  if (!ctx || ctx.__attached) return;   // 附加来的,不动它
  try { await ctx.close(); } catch (e) {}
}

如果一定要杀进程,优先用 CloseMainWindow() 而不是 Stop-Process -Force,给它一个体面退出的机会。

对策二:CDP 附加复用,少起进程

既然启动/关闭是风险点,那就尽量少开关浏览器。Playwright 启动时带上调试端口:

const ctx = await chromium.launchPersistentContext(profileDir, {
  args: ['--remote-debugging-port=9333', ...],
});

后续所有脚本优先尝试附加到已运行的实例:

async function open(opts) {
  // 先探测有没有在跑的浏览器
  try {
    const browser = await chromium.connectOverCDP('http://127.0.0.1:9333', { timeout: 4000 });
    const ctx = browser.contexts()[0];
    if (ctx) { ctx.__attached = true; return ctx; }   // 直接复用
  } catch (e) { /* 没有,走正常启动 */ }

  return await chromium.launchPersistentContext(profileDir, {...});
}

好处很直接:浏览器只启动一次,多个脚本轮流复用,Session Cookie 全程留在内存里,压根没有丢失的机会。

对策三:兜底——自动重新登录

前两条是"防",这条是"兜"。会话真失效了,脚本要能自己恢复,而不是直接报错退出:

// 用「必须登录才能访问」的页面做硬检测,比看 Cookie 可靠
async function checkLogin(ctx) {
  const page = ctx.pages()[0];
  await page.goto('https://example.com/dashboard');
  const hasEditor = await page.evaluate(() => typeof window.appReady !== 'undefined');
  const redirected = /\/login/i.test(page.url());
  return { loggedIn: hasEditor && !redirected };
}

// 检测 → 失效则登录 → 再验证
let chk = await checkLogin(ctx);
if (!chk.loggedIn) {
  await login(ctx);        // 自动填账号密码、处理验证弹窗
  await save(ctx, page);   // 立刻固化会话
  chk = await checkLogin(ctx);
}

这里有个容易踩的坑:不要用 Cookie 是否存在来判断登录态。

我们前面已经看到,cbc-sid 还在的时候页面照样是未登录的。用「访问需要登录的页面,看是否被重定向」才是可靠判据。


第四步:验证与固化

修好之后,验证一下三条链路是否都通:

// 1) 优雅关闭后重启,登录态是否还在?
// 2) 附加模式下,另一个脚本能否复用同一会话?
// 3) 手动清掉 Session Cookie 模拟失效,自动重登能否救回来?
const r = await login(ctx, { waitHumanMs: 900000 });
console.log('登录结果:', r);   // { ok: true, url: '...' }

另外,每次运行结束都顺手把会话快照存一份:

await ctx.storageState({ path: './session/state.json' });

storageState 会把包括 Session Cookie 在内的所有 Cookie 序列化下来。虽然浏览器正常关闭时这些 Cookie 会丢,但有了这份快照,启动时可以主动回灌:

const st = JSON.parse(fs.readFileSync('./session/state.json', 'utf8'));
if (st.cookies?.length) await ctx.addCookies(st.cookies);

持久化目录 + 快照回灌 + 自动重登,三层叠加之后,登录态才算真正稳了。


小结

环节 做法 作用
关闭 ctx.close() / closeQuiet() 保住 Session Cookie
运行 CDP 附加复用 减少启停,会话常驻内存
检测 访问需登录页看重定向 比查 Cookie 可靠
兜底 自动重新登录 + 立刻固化 失效也能自愈

三条经验:

  1. "Cookie 还在"不等于"登录态还在" —— 持久 Cookie 只是设备标识,真正的凭证往往是 Session Cookie。
  2. kill -9 是自动化里的隐形杀手 —— 它绕过一切清理逻辑,包括你依赖的状态持久化。
  3. 判据要选"行为"而不是"状态" —— 看页面是否被重定向,比看 Cookie 是否存在靠谱得多。

本文为实践记录,代码片段已做脱敏处理。不同站点的 Cookie 策略差异较大,具体以实际抓包结果为准。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。