首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Sa-Token 记住我机制,讲透 Cookie 和 Token

Sa-Token 记住我机制,讲透 Cookie 和 Token

作者头像
程序员NEO
发布2026-04-29 19:37:47
发布2026-04-29 19:37:47
1110
举报
你有没有遇到过这种情况,登录页明明有个“记住我”,测试环境也跑通了,结果一上线,用户第二天还是来问一句:怎么又要重新登录? 我第一次接这个需求时,也以为这只是前端多一个勾选框。后来真把 Sa-Token 和项目场景放在一起看,才发现这事一点都不“小”。它本质上不是页面功能,而是 登录态存在哪里、活多久、浏览器关掉之后还认不认

这篇文章,我就结合 Sa-Token 的实现,把这件事彻底拆开。如果你们项目里也在做 Web 登录、前后端分离、APP / 小程序鉴权,这篇会很有用。

为什么“记住我”总容易被做成半成品

说实话,这种需求在 Demo 里特别好做。产品说要一个“记住我”,前端传个布尔值,后端接一下,页面上看着就像完成了。

但到了真实项目里,问题马上就会冒出来:

  • 浏览器端和前后端分离端,能力根本不是一回事。
  • 用户以为“记住我” = 永远不用再登录。
  • 研发以为“记住我” = 把 Token 多放几天。
  • 安全侧又会担心共享电脑、公共设备、长期登录带来的风险。

我后面越来越觉得,很多团队不是不会写这个功能。而是从一开始,就把问题理解窄了。

“记住我”真正要回答的,不是要不要勾选,而是登录态的生命周期怎么设计。

在 Sa-Token 里,一个很多人第一次就会看反的点

Sa-Token 这套设计里,登录授权默认就是 [记住我] 模式

也就是说,如果你想实现的是“浏览器关掉后,下次必须重新登录”,你反而要在登录时显式写出来:

代码语言:javascript
复制
// 设置登录账号id为10001,第二个参数指定是否为[记住我],当此值为false后,关闭浏览器后再次打开需要重新登录
StpUtil.login(10001, false);

这个点我第一次看到时,确实愣了一下。因为很多人脑子里的默认认知是:

  • • 不勾选,才是默认行为。
  • • 勾选之后,才开启“记住我”。

但在工程实现层面,Sa-Token 更像是把“登录后默认保持状态”当成基础能力。你如果要“非记住我”,就显式关闭它。

这其实也提醒我们一件事:业务语义和框架默认值,最好不要靠猜。

它的原理不复杂,难的是别把两个概念混在一起

浏览器这边,记住我 的底层并不玄学。本质上就是 Cookie 生命周期的区别。

Cookie 作为浏览器默认的会话跟踪机制,生命周期通常就两种:

  • 临时Cookie:当前会话有效,浏览器窗口一关,Cookie 就没了。
  • 持久Cookie:有明确过期时间,只要没过期,浏览器重开后还在。

所以 Sa-Token 这件事的实现逻辑非常直接:

  • • 勾选 [记住我] 时,调用 StpUtil.login(10001, true),写入的是 持久Cookie
  • • 不勾选 [记住我] 时,调用 StpUtil.login(10001, false),写入的是 临时Cookie

到这里为止,逻辑其实很顺。但真正容易出事的,是很多人会把下面两个问题混成一个:

  • 浏览器关掉后,凭证还在不在?
  • 这个 Token 在服务端还能活多久?

这两个问题有关,但不是一回事。后面我们专门说。

真正的坑,往往出在前后端分离

我当时踩到的第一个坑,就在这里。浏览器里用 Cookie 做“记住我”很自然。可一旦切到 APP、小程序、uni-app 这类前后端分离场景,Cookie 这条路就走不通了。

原因也很现实:这些客户端默认并没有完整实现浏览器那套 Cookie 会话机制。

这时候,你就得自己接管 Token 的存储策略。以 uni-app 为例,原始思路是这样的:

代码语言:javascript
复制
// 使用本地存储保存token,达到 [持久Cookie] 的效果
uni.setStorageSync("satoken", "xxxx-xxxx-xxxx-xxxx-xxx");

// 使用globalData保存token,达到 [临时Cookie] 的效果
getApp().globalData.satoken = "xxxx-xxxx-xxxx-xxxx-xxx";

如果是 PC 浏览器里的前后端分离项目,一般会更直接一点:

代码语言:javascript
复制
// 使用 localStorage 保存token,达到 [持久Cookie] 的效果
localStorage.setItem("satoken", "xxxx-xxxx-xxxx-xxxx-xxx");

// 使用 sessionStorage 保存token,达到 [临时Cookie] 的效果
sessionStorage.setItem("satoken", "xxxx-xxxx-xxxx-xxxx-xxx");

这套映射很好理解。但真上项目时,我更建议你把下面几件事提前说清楚:

  • 前端是否真的按勾选状态切换了存储位置。
  • 切换账号时,持久存储和会话存储是否都清干净了。
  • APP、小程序、H5 这几个端,对“关闭应用”这件事的定义是不是一致。

我见过最常见的一种翻车方式,就是前端图省事,直接把 Token 一律塞进 localStorage。这样一来,页面上虽然还有“记住我”按钮,但业务语义其实已经失真了。

也就是说,用户以为自己没勾选,系统却还是把他当成了长期登录。

这类问题最烦的地方在于,它不一定报错。它只是让“按钮文案”和“真实行为”慢慢分家。

我后来才想明白:记住我,不等于 Token 长期有效

这个认知点,我觉得特别重要。很多团队第一次做这个功能时,会天然把“记住我”和“延长 Token 有效期”绑定在一起。但 Sa-Token 其实把这两个维度拆开了。

比如你完全可以单独指定 Token 有效期:

代码语言:javascript
复制
// 示例1:
// 指定token有效期(单位: 秒),如下所示token七天有效
StpUtil.login(10001, new SaLoginParameter().setTimeout(60 * 60 * 24 * 7));

// ----------------------- 示例2:所有参数
// `SaLoginParameter`为登录参数Model,其有诸多参数决定登录时的各种逻辑,例如:
StpUtil.login(10001, new SaLoginParameter()
            .setDevice("PC")                // 此次登录的客户端设备类型, 用于[同端互斥登录]时指定此次登录的设备类型
            .setIsLastingCookie(true)        // 是否为持久Cookie(临时Cookie在浏览器关闭时会自动删除,持久Cookie在重新打开后依然存在)
            .setTimeout(60 * 60 * 24 * 7)    // 指定此次登录token的有效期, 单位:秒 (如未指定,自动取全局配置的 timeout 值)
            .setToken("xxxx-xxxx-xxxx-xxxx") // 预定此次登录的生成的Token 
            .setIsWriteHeader(false)         // 是否在登录后将 Token 写入到响应头
            );

这里面至少有两个参数,语义完全不同:

  • setIsLastingCookie(true):控制的是 Cookie 是不是持久化,影响“浏览器关掉再打开后还在不在”。
  • setTimeout(...):控制的是 Token 的有效时长,影响“服务端认不认这个 Token”。

这两个维度一拆开,很多线上现象就能解释通了:

  • 持久Cookie + 短 Token 有效期

用户重开浏览器后,本地凭证还在,但服务端可能已经判它过期了。

  • 临时Cookie + 长 Token 有效期

服务端本来还认这个 Token,但浏览器一关,本地凭证已经丢了,用户照样得重新登录。

所以我后面跟产品或者测试对这个需求时,会尽量不用一句模糊的“支持记住我”。而是直接问清楚两件事:

  • • 浏览器关闭后,用户下次打开还要不要自动带登录态?
  • • 如果要带,这个登录态最多允许持续多久?

你把这两件事问清楚,需求至少一半不会跑偏。

真要上生产,我更建议这样落

如果只是做个 Demo,写到这里其实已经够了。但如果是线上系统,尤其是后台、SaaS 控制台、运营平台,我更建议你多补几层工程兜底。

我自己的经验里,至少要补这 5 件事:

  • 明确“记住我”的业务边界。C 端用户、B 端员工、共享电脑、公共终端,这几个场景默认策略不一定一样。
  • 前后端统一语义。前端的勾选状态、Token 存储位置、后端登录参数,三者必须对得上。
  • 不要把“记住我”写成“永久在线”。长期在线只是用户感知,真正实现上还是要有明确过期时间。
  • 退出登录时清理干净。不只是服务端注销,前端本地持久化数据也要同步删除。
  • 把安全成本想在前面。特别是管理后台,如果默认勾选“记住我”,很多时候并不是更友好,而是更危险。

这里我有个偏主观但挺实战的判断:

  • 面向普通 C 端用户的高频产品,可以给“记住我”更友好的默认体验。
  • 面向后台、财务、运营、管理端的系统,默认保守一点,往往更稳。

因为后台系统的设备环境通常更复杂,账号交叉使用、代操作、共享电脑的概率也更高。这时候你给了一个“长期保持登录”的按钮,后面补安全洞和补说明文案的成本,常常比你想象得高。

最后说一句我现在很认同的话

以前我会把“记住我”看成登录页上的一个小功能。现在我更愿意把它看成 登录态生命周期策略 的一个入口。

它真正决定的是:

  • • 凭证放在哪里。
  • • 浏览器关闭后保不保留。
  • • 服务端认多久。
  • • 前后端怎么配合。
  • • 用户掉登录时,体验是不是可解释。

代码当然不复杂。难的是你别把一个看起来很小的按钮,做成一个语义模糊、行为失控、出了问题没人能说清的功能。

说实话,很多认证问题都不是代码难,而是概念一开始就定义错了。

工程金句

“记住我”不是一个表单勾选项,它本质上是在定义:登录态存在哪里、活多久、浏览器重启后还认不认。

你们项目里的“记住我”,现在是怎么做的?

  • • 还是传统 Cookie 方案?
  • • 还是前后端分离自己接管 Token 生命周期?
  • • 你们有没有踩过“明明没勾选,却还是长期在线”或者“本地还在,服务端却过期了”的坑?

欢迎在评论区分享你们的经验和教训!

相关文章推荐

如果这篇文章帮到了你,不妨点个分享给同样需要的朋友吧! 你的每一次支持,都是我持续创作的动力!💪

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-03-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员NEO 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么“记住我”总容易被做成半成品
  • 在 Sa-Token 里,一个很多人第一次就会看反的点
  • 它的原理不复杂,难的是别把两个概念混在一起
  • 真正的坑,往往出在前后端分离
  • 我后来才想明白:记住我,不等于 Token 长期有效
  • 真要上生产,我更建议这样落
  • 最后说一句我现在很认同的话
  • 工程金句
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档