把会话 Cookie 改成 30 天,为什么还是会掉登录

举报
yd_276135645 发表于 2026/10/28 06:04:46 2026/10/28
【摘要】 浏览器重启就掉登录,通常是因为会话 Cookie 不落盘。本文讲清怎么把 expires=-1 改成 30 天并写回运行中的浏览器,以及三个更隐蔽的坑:restore 时序、标签页恢复覆盖、残留的死 CASTGC 让登录页陷入死循环。

我在一台没有显示器的服务器上跑着常驻 Chromium,通过 CDP 让 Playwright 接管它,每天定时做几件需要登录才能做的事。登录这件事本来很简单——用户从 noVNC 里手动输入一次验证码,之后就都好了。问题是"之后"并不久。

第一次发现异常很直观:浏览器进程重启一次,登录态就没了。查下来原因也不复杂——华为云那套 CAS 单点登录里,CASTGC、SSOJTC、HUAWEIID_CAS_ISCASLOGIN 全都是会话 Cookie,expires 是 -1。会话 Cookie 只活在内存里,Chrome 正常退出时不会被写进 profile 目录下的 Cookies SQLite 库,进程一退就是彻底消失。

于是有了第一个想法,也是这篇文章真正要讨论的事:如果把会话 Cookie 的过期时间改成 30 天,是不是就能免疫重启了?

一、cookie 持久化的正确做法

只用 context.cookies() 把 Cookie 存成 JSON 是不够的。存下来的 JSON 只是一份快照,profile 里躺着的仍然是 expires=-1,Chrome 下次启动照样丢掉。所以必须做两件事,缺一不可:

  1. 把 expires == -1 的项改成一个具体的未来时间戳;
  2. 写回正在运行的浏览器(context.add_cookies 或 CDP 的 Network.setCookie),让 Chrome 自己走一遍持久化逻辑落到 profile 里。

第二步是最容易漏的。只改 JSON 不写回浏览器,看起来"我明明保存了",重启后却依然掉登录——因为落盘的是 Chrome 的内部状态,不是你手里的那份 JSON。

写成脚本大概是这个结构:

cookies = ctx.cookies()
fixed = []
for c in cookies:
    c = dict(c)
    if c["expires"] in (-1, None):
        c["expires"] = time.time() + 30 * 86400
    fixed.append(c)
ctx.add_cookies(fixed)          # 关键:回写运行中的浏览器
json.dump(fixed, open(store, "w"))

再配一个每 15 分钟跑一次的 systemd timer 做快照,重启时用 ExecStartPost 把 Cookie 灌回去。到这里,"浏览器重启就掉登录"这个问题确实解决了——实测重启后 LOGGED_IN。

二、比持久化更麻烦的三个坑

坑一:restore 的时序

Chrome 刚起来的时候,页面还停在 about:blank,document.body 是 null。这时候急着 add_cookies 是一半成功一半白干:CDP 调用可能返回成功,但紧接着 Chrome 恢复上次的标签页,那个动作会覆盖掉刚灌进去的 Cookie。

正确顺序是:先等到 document.readyState === "complete" 且 document.body 存在,再 add_cookies,然后才让页面去导航。听起来像玄学,其实很好理解——你在它还没初始化完的时候塞东西,它初始化的时候会把你的东西冲掉。

坑二:判活判错了对象

Cookie 里有两个长得很像的东西:CASTGC 和 SSOJTC。

  • CASTGC 是 CAS 的票据 Cookie,服务端会话死了它还会残留,看上去一直"有效";
  • SSOJTC 只在会话真正有效时才存在。

我一开始拿 CASTGC 当判据,结论是"登录态正常",然后所有需要登录的请求全部被重定向到登录页。判活只能看 SSOJTC。

坑三:死 Cookie 制造出的登录死循环

这是最坑的一个,症状是用户说"我怎么都登录不了"。

服务端会话过期后,如果 restore 逻辑老老实实把那份"有效期还有 30 天"的旧 CASTGC 灌回浏览器,登录页会进入这样一个循环:

检测到有会话 → 去服务端校验 → 校验失败 → 弹回登录页 → 又检测到有会话 → …

页面本身完全正常,账号密码也对,但人就是登不进去。根因是那份被人工延长了有效期的死 Cookie:它把"我没有会话"这个事实掩盖了。

修法很短:一旦发现 SSOJTC 缺失,立刻把 CASTGC、HUAWEIID_CAS_ISCASLOGIN、SSOJTC、CASLOGINSITE、LOGINACCSITE 全部删掉——浏览器里的和存档里的一起删。宁可让用户老老实实登一次,也不要留一份会骗人的 Cookie。

三、这套方案的上限

必须说清楚:cookie 持久化只解决"浏览器重启",解决不了"服务端会话过期"。

实测数据:在没有任何重启的情况下,会话也只活了大约 20 小时。也就是说,不管你把 cookie 的有效期改成 30 天还是 3 年,服务端那边的会话一到期,本地这份 Cookie 就只是一张过期的门票。

所以这套东西的真实价值是把重登频率从"每次重启掉一次"压到"每天最多一次",而不是"永久登录"。不要向任何人承诺永久登录,也不要把"今天没掉"当成机制修好了——那只是今天运气好。

四、比追求永久登录更值得做的事

既然上限摆在那里,工程上的注意力应该换一个方向:

1. 把"掉线"变成一条会主动告诉你的消息。 一个每 10 分钟跑一次的探针,只在状态翻转时推送:in → out 推一次"登录态失效",out → in 推一次"已恢复",中间完全静默。这样你不需要盯着,也不会被"一切正常"淹掉。

2. 三信号联合判定,别信单一指标。 我在踩坑过程中遇到过三种假信号:

  • 某社区站点会用缓存渲染出"看着已登录"的昵称和内容,但上游 CAS 早就死了;
  • 页面导航栏偶尔渲染出未登录态的"登录"链接,其实登录是好的;
  • 反过来,一个自己写的判定脚本会在页面慢渲染时误报"未登录",白白把整次任务停掉。

结论是:页面文本 + 关键接口是否被重定向到登录页 + 关键 Cookie 是否存在,三个信号一起看,缺一个都不下结论。

3. 用日志留一条时间线。 那个每 15 分钟的快照任务同时在日志里打一行 Cookie 指纹。排障时最有用的一句话就是:“SSOJTC 是从哪一分钟开始不在列表里的”——那一分钟就是会话真正死亡的时刻,能立刻区分"服务端过期"还是"浏览器重启",而这两者的排查方向完全不同。

写在最后

这件事真正的教训不是某个 API 怎么调,而是一个判断上的错误:把客户端状态当成了服务端状态。

Cookie 是本地的,会话是服务端的,两者的生命周期没有任何强制绑定关系。凡是"延长本地凭据有效期"的方案,都要先问一句:服务端认不认这份凭据,认多久。想清楚这一点,就不会去追求那个不存在的"永久登录",而是老老实实做监控、做恢复、把预期管理好——这比任何技巧都值钱。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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