用户不在时把门锁好:vibe 项目的会话超时与自动登出实战
用户离开一会儿,系统就替他把门锁上——但别把他写的东西扔掉。闲置超时、绝对有效期、敏感操作重验证与草稿恢复,vibe 项目一次讲清。


以下示例产品与人物均为虚构,仅用于说明做法。假设你做了一个叫“白鹭书店”的线上商城,店员小满每天要在店里的共用电脑上处理订单:查库存、改价格、给客户发提货通知。一天中午,小满去仓库搬了一箱书,回来发现自己还登录着——这半小时里,任何一个路过这台电脑的人都能以她的身份改价格、看客户电话。更麻烦的是,另一位店员在小满的会话还没过期时误点了“确认发货”,发错了两单货。这篇指南讲的就是怎么给 vibe 项目设计“会话超时与自动登出”:让用户离开一会儿,系统就自动把门锁上;同时又不至于让正在认真填表单的人被无缘无故踢出去。
先分清两个经常被混为一谈的时间。第一个是闲置超时:用户多久没有操作,系统就判定他离开了。第二个是绝对有效期:不管用户多活跃,登录凭证从签发那一刻起最多活多久。很多项目只做了前者,结果一个用户只要每隔几分钟动一下鼠标,会话就能活好几天;只做后者也不行,一个正在写长文章的用户会在第 8 个小时被准时踢出,草稿都没保存。合格的做法是两个时间同时存在:闲置超时管“人走了没有”,绝对有效期管“这次登录活了多久”,两个条件哪个先到,就按哪个处理。至于具体数字,后面会按敏感度分级给建议,不要全站只用一个 30 分钟了事。
分级是整篇指南最重要的决策。不同页面、不同操作,丢会话的代价完全不同。我建议分三档。第一档是普通浏览:看商品、读文章、翻订单列表,这类操作不改数据,闲置 30 分钟再登出也无妨,体验优先。第二档是常规写操作:改收货地址、写评价、填表单,闲置 15 分钟登出比较合理,登出前要提醒、登出后要保留草稿。第三档是敏感操作:改价格、退款、看客户电话、改权限、导出数据,这类操作一旦被冒用损失很大,闲置 5 分钟就应当登出,或者更进一步——每次执行敏感操作前都要求重新验证身份(输一次密码或指纹),而不是只依赖会话还活着。白鹭书店的例子里,查库存可以是第一档,改价格和确认发货必须是第三档;如果当初这样分过级,小满去仓库那半小时,系统早就把敏感操作锁上了。
登出提醒怎么做,是体验的分水岭。最差的做法是毫无预兆地踢人:用户正在填一个长表单,回来发现被登出、表单全丢了,他下次就不会再认真填了。正确的做法是提前提醒、给续期机会:闲置时间快到时(比如还剩 2 分钟),页面弹一个不遮挡全部内容的提示条,写清楚“你已经 13 分钟没有操作,2 分钟后将自动登出”,并给两个按钮——“继续使用”和“立即登出”。点“继续使用”应当立即续期,不需要重新登录;点“立即登出”则马上结束会话。提醒本身也要注意节制:只在用户回到页面时出现,不要用弹窗轰炸正在操作的用户;如果用户正在输入,提醒条不要抢夺输入框焦点,更不要在用户打字打到一半时把页面刷新掉。
续期按钮背后有一个安全细节:续期不能无上限。如果每次点“继续使用”都能无限续期,闲置超时就名存实亡了——攻击者只要定时点一下,就能让会话永远活着。所以续期要受绝对有效期的约束:比如绝对有效期 8 小时,从登录那一刻起算,无论续期多少次,8 小时一到都必须重新登录。实现时可以这样记:会话里存两个时间戳,一个是最后活动时间(每次操作都更新),一个是登录时间(只在登录时写一次);每次请求都算两笔账,闲置超了按闲置处理,绝对超了按绝对处理,两个判断都要在服务端做,不能只靠前端定时器。
登出之后,用户的东西不能丢
自动登出最伤体验的地方,是用户回来发现自己写的东西没了。解决办法是把“登出”和“丢数据”彻底分开。表单草稿应当在用户输入时就自动保存到本地(浏览器本地存储),登出不会清除这些草稿;用户重新登录后,打开同一张表单,系统应当问一句“检测到你上次没写完的草稿,要恢复吗”,而不是悄悄丢弃或者悄悄覆盖。购物车、筛选条件、正在看的页面位置,同理:能恢复的都恢复,不能恢复的要明说。白鹭书店的场景里,如果小满正在给一单写备注时被登出,重新登录后备注应当还在——这是自动登出功能合格与否的试金石。
但本地草稿也有边界。敏感内容不要长期留在本地:客户电话、身份证号、支付信息这类数据,登出时应当从本地存储里清除,而不是留着等下次恢复。判断标准很简单:如果这条数据被下一个用这台电脑的人看到会不会出事,会出事就不要放进本地草稿。普通内容(商品评价草稿、订单备注、筛选条件)可以留,敏感内容必须清。还有一种情况要处理:用户在公共电脑上点了“记住我”——自动登出时应当同时清除“记住我”的凭证,或者至少在登出提示里写清楚“这台是公共电脑,建议不要勾选记住我”。很多安全事故不是技术没做,而是提示没写。
多设备场景也要想清楚。用户在手机上登录着,电脑上被自动登出了,手机应当不受影响——因为那是两个独立的会话。但有一种情况必须联动:如果用户在某台设备上手动点了“退出所有设备”,或者系统检测到异常登录(比如新设备、新城市),就应当让所有会话一起失效,并给用户发一条通知说明原因。自动登出本身不需要联动所有设备,但“退出所有设备”这个功能必须存在,而且要放在用户找得到的地方(账号设置里的“登录设备”一栏),不要藏在三级菜单里。白鹭书店如果给店员账号配了“查看登录设备、踢掉不认识的设备”的功能,小满那次误操作之后,店长就能立刻把共用电脑上的会话踢掉,而不是等它自己超时。
服务端说了算,前端定时器只是提醒器
实现上有条铁律:会话是否有效,永远由服务端说了算。前端可以做一个倒计时、弹提醒条、到点跳登录页,但这些都只是体验层;真正的判断必须发生在每次请求到达服务端时——服务端查会话的最后活动时间与登录时间,过期就返回未登录状态,前端再按这个状态处理。只靠前端定时器的做法有一个致命漏洞:用户关掉提醒、改系统时间、或者直接调接口,都能绕过登出。更隐蔽的漏洞是“心跳续期”:有些项目为了让提醒条准确,前端每分钟发一次心跳请求,而服务端把任何请求都当作活动——结果心跳本身让会话永远不会闲置超时,自动登出彻底失效。正确的做法是:心跳请求明确标记为不续期,只有真正的业务请求(读数据、提交表单)才更新最后活动时间。
接口层面的处理也要统一。会话过期后,接口应当返回一个明确的未登录状态(比如 401),而不是笼统的“请求失败”或空数据。前端收到 401 后的处理要分情况:如果是用户正在看的页面,跳登录页并记住当前地址,登录后跳回来;如果是后台静默请求(比如自动保存草稿),不要粗暴地弹登录框打断用户,而是先把草稿存到本地,再提示“登录已过期,草稿已保存在本地,重新登录后可恢复”。移动端同理:App 被切到后台再回来,会话可能已经过期,这时不要直接闪退到登录页,先恢复界面、再温和地提示重新登录。
时钟与时区是另一个容易踩的坑。闲置时间应当用服务端时间算,不要用客户端时间——用户改一下本机时间就能把闲置计时改乱。如果你的服务部署在多个时区,统一用 UTC 存时间戳,展示时再换算。还有一种边界情况:用户在登出提醒弹出的那一刻正好提交了表单,请求到达服务端时会话刚好过期——这时应当返回“会话已过期、表单未提交”,而不是“提交成功”或含糊的报错,并且要告诉用户表单内容已经保存在本地草稿里。提交与过期撞车的体验,决定了用户会不会信任你的自动登出。
敏感操作前,先再验一次身份
分级里第三档还有一半没讲完:对最敏感的操作,只靠“会话还没闲置超时”是不够的,应当在操作执行前再验一次身份,这就是 step-up 验证。触发条件要列得具体:改价格、退款、提现、导出客户数据、改他人权限、删核心数据、改收款账号——这些操作无论会话还剩多久,都先弹一次验证(密码、指纹、短信码、验证器 App,任选一种你已经接了的方式)。验证通过后,给一个短暂的“高权限窗口”,比如 10 分钟,窗口内再做同类敏感操作不用重复验证;窗口一过,或者用户切了页面、闲置了一会儿,就要重新验证。
这里有三个细节容易做错。第一,验证失败的处理:连续输错几次密码后,应当锁定验证入口一段时间,而不是无限次重试——否则 step-up 验证本身就成了可爆破的口子。第二,验证状态不能只存在前端:高权限窗口的起止时间要存在服务端,会话里记一个“上次通过敏感验证的时间”,每次敏感操作都由服务端核对,过期就拒绝并要求重新验证。第三,验证通过后的操作要留痕:谁、在什么时间、从哪个设备、做了哪项敏感操作,写进审计日志——这条日志和审计日志指南里讲的做法是同一套,不要为 step-up 单独发明一套日志格式。白鹭书店的场景里,如果“确认发货”属于敏感操作,小满去仓库前会话即使还活着,误点的店员也会先撞上一次密码验证,发错货的概率就小得多。
还有一种边界情况:用户刚刚登录进来,紧接着就做敏感操作,还要不要再验一次?我的建议是:登录后 5 分钟内做敏感操作可以免验,因为登录本身就是一次新鲜验证;超过 5 分钟,就按 step-up 规则来。这个 5 分钟的“新鲜度窗口”要在产品里写清楚,不要让用户觉得规则飘忽不定——有时候要验、有时候不要,用户会以为系统出 bug 了。
不同产品,数字应该不一样
前面给的三档数字(30、15、5 分钟)是通用起点,不同产品要按自己的风险调。下面是四类产品的调法。第一类是内容与工具类 SaaS(笔记、看板、文档):用户经常开着页面查资料,闲置 30 分钟登出、绝对有效期 12 小时是比较舒服的组合,提醒提前 2 分钟就够。第二类是电商与商城:浏览可以放宽到 30 分钟,但下单、改地址、改支付方式这类写操作要收到 15 分钟档,购物车本身不要因为登出而清空——购物车是商品数据,不是会话数据,登出后重新登录应当还在。
第三类是金融与支付相关:这档要收紧得多。浏览 15 分钟、写操作 5 分钟、敏感操作每次验证,绝对有效期 4 小时,而且“记住我”默认不勾选、甚至直接不提供。用户会觉得麻烦,但金融产品的用户对“麻烦”的容忍度恰恰最高——他们宁愿多验一次,也不愿看到账户在公共电脑上还登录着。第四类是内部工具与后台管理系统:这是白鹭书店那类场景,特点是共用电脑多、操作敏感度高。建议浏览 15 分钟、写操作 10 分钟、敏感操作 5 分钟加 step-up 验证,绝对有效期 8 小时(一个工作日),并且强制要求“登录设备”列表可见、支持一键踢掉其他设备。内部工具的管理员不要图省事把超时设成 8 小时不登出——共用电脑上的 8 小时,等于 8 小时的敞口。
数字定下来之后,要写进产品的帮助文档或设置页里,让用户找得到。很多产品的自动登出之所以被用户骂,不是因为数字定错了,而是因为用户根本不知道规则:为什么刚才还好好的,突然就被登出了?在设置页里写一句“出于安全考虑,无操作 15 分钟后将自动登出,登录一次最长保持 8 小时”,再配一个“延长会话”或“记住这台设备”的显式选项,用户的不满会少一大半。规则透明,本身就是体验的一部分。
“记住我”不是免死金牌
最后讲“记住我”与自动登出的配合,这是最容易写错关系的一对。记住我解决的是“下次打开还要不要登录”,自动登出解决的是“这次登录活多久”,两者管的是不同的时间尺度,正确的配合是:记住我可以让用户下次打开时免登录,但不能让单次会话无限延长——绝对有效期到了,记住我也救不了,必须重新登录。更精细的做法是分设备信任:用户明确标记为“这是我的私人电脑”的设备,记住我可以生效 30 天;没有标记过的设备(尤其是公共电脑),记住我默认不生效,或者只生效 24 小时。
实现上,记住我应当用一张独立的、可吊销的长期凭证,而不是把会话凭证的有效期直接拉长。这样做的好处是:用户在账号设置里点“退出所有设备”时,长期凭证和会话凭证可以一起作废;而如果只靠拉长会话有效期,退出所有设备就很难做干净。另外,记住我的凭证被使用时(也就是用户靠它免登录进来时),如果设备、城市、IP 发生了明显变化,应当降级处理:允许浏览,但敏感操作必须重新验证,并给用户发一条“新设备登录提醒”。这条提醒不是打扰,而是给用户一个发现异常的机会——很多盗号事件,都是用户先在提醒里发现不对劲的。
测试清单:上线前把这些场景都跑一遍
自动登出功能上线前,建议按这份清单逐项实测。第一,闲置登出:打开页面不动,分别在 5 分钟、15 分钟、30 分钟三档下确认登出时间是否符合分级,提醒条是否在到期前 2 分钟出现。第二,续期:点“继续使用”后确认会话确实续期了,且绝对有效期没有被顺带延长。第三,绝对过期:登录后持续操作超过绝对有效期,确认系统仍然要求重新登录,而不是无限续期。第四,草稿恢复:填一半表单被登出,重新登录后确认草稿能恢复,敏感字段没有残留在本地。第五,多设备:电脑登出后手机会话不受影响,手动“退出所有设备”后所有会话一起失效。第六,心跳陷阱:只开着页面不动(只有心跳请求),确认闲置超时仍然会触发,而不是被心跳无限续期。第七,公共电脑:勾选“记住我”后自动登出,确认记住我的凭证已被清除或有明确提示。
最后一节写给赶时间的你,列出五个反模式。第一,只靠前端定时器登出,服务端不做判断——等于门上贴了张“请勿进入”的纸条,却没有锁。第二,续期无上限,绝对有效期形同虚设——闲置超时就被架空了。第三,登出不吊销刷新凭证:访问令牌过期了,刷新令牌还能换新的,等于登了个假出;登出时必须同时作废刷新凭证。第四,敏感操作与普通浏览同一档超时——改价格和看商品用同一个 30 分钟,等于把金库和大门用同一把锁。第五,登出后不给用户任何交代:表单丢了、购物车空了、连一句“你已被自动登出”都没有——用户不会觉得系统安全,只会觉得系统有 bug。自动登出的目标从来不是“把用户赶走”,而是“在用户不在的时候,把门锁好;等用户回来,把东西原样还给他”。
评论 (0)
相关文章

Claude Code v2.1.311 新增环境变量,删文件前最后一道确认也可以关掉了。默认行为没变、开关纯属自愿,但一旦打开,模型的误判就再也没有人工检查点。本文讲清权限阶梯、哪些人绝对不要碰它,以及非用不可时的五条硬规矩。

一人项目最常见的死法不是做不出来,而是放下就拿不起来。本文讲复工手册五件套、怎么写给失忆的自己、AI 协作断档怎么接、复工第一天的固定顺序,附验收清单。

没有预算,优化永远做不完也守不住。本文讲第一版性能预算怎么定、怎么写进 CI 门禁、超标时四种还法怎么选、AI 功能为什么要单独算,附上线验收清单。