news 2026/9/14 8:06:48

Sa-Token「记住我」模式全解析:临时 Cookie、持久 Cookie 与登录参数定制实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Sa-Token「记住我」模式全解析:临时 Cookie、持久 Cookie 与登录参数定制实战

Sa-Token「记住我」模式全解析:临时 Cookie、持久 Cookie 与登录参数定制实战

【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架,让鉴权变得简单、优雅!—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token

几乎所有网站登录界面上都有一个「记住我」复选框:勾选后,即使关闭浏览器再重新打开,依旧保持登录状态,无需重复输入密码。本篇文章聚焦 Sa-Token 框架中「记住我」模式的完整实现:从默认行为、两种 Cookie 生命周期的底层原理,到前后端分离环境(APP、小程序、PC 浏览器)下的替代方案,再到利用SaLoginParameter精细控制 Token 有效期与登录细节。读完本文,你将掌握用一条login()调用精确掌控会话寿命的完整实战能力。

一、Sa-Token 中的「记住我」默认就是开启的

在 Sa-Token 中,「记住我」并不是一个需要额外开启的功能,恰恰相反——Sa-Token 的登录授权默认就是「记住我」模式

也就是说,日常最常见的这种写法:

StpUtil.login(10001);

等价于「勾选了记住我」的登录:登录成功后框架会在浏览器写入一个持久 Cookie,用户关闭浏览器再打开,会话依然有效。

这一点在源码中有明确依据:SaTokenConfig.java 中isLastingCookie字段的默认值为true,同时默认timeout(Token 有效期)为60 * 60 * 24 * 30,即 30 天(同文件第 47 行)。

而要实现「非记住我」模式,只需在登录时显式传入第二个参数:

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

二、实现原理:临时 Cookie 与持久 Cookie

Sa-Token 之所以能用一行代码切换「记住我」与「非记住我」,靠的是 Cookie 作为浏览器会话跟踪机制的两种生命周期形式

  • 临时 Cookie:有效期为本次会话,只要关闭浏览器窗口,Cookie 就会消失。
  • 持久 Cookie:有效期为一个具体的时间段,在时间未到期之前,即使用户关闭浏览器,Cookie 也不会消失。

利用这一特性,「记住我」的实现逻辑非常直观:

用户操作框架调用写入浏览器的 Cookie重启浏览器后的效果
勾选「记住我」StpUtil.login(10001, true)持久 Cookie 储存 TokenToken 依然有效,保持登录
不勾选「记住我」StpUtil.login(10001, false)临时 Cookie 储存 TokenToken 随之消失,会话失效

源码级佐证:Cookie 的存活时长是如何计算的

从源码看,登录后的 Token 注入流程位于 StpLogic.java 的setTokenValue(tokenValue, loginParameter)方法:框架会将 Token 依次写入 Storage 存储器、浏览器 Cookie,并在需要时写入响应头。

其中写入 Cookie 时使用的存活时长,由 SaLoginParameter.java 中的getCookieTimeout()方法计算:

public int getCookieTimeout() { // 非持久 Cookie:返回 -1,代表内存级 Cookie,浏览器关闭即消失 if (!Boolean.TRUE.equals(getIsLastingCookie())) { return -1; } long _timeout = getTimeout(); // 持久 Cookie:直接使用 Token 的有效期作为 Cookie 存活时长 if(_timeout == SaTokenDao.NEVER_EXPIRE || _timeout > Integer.MAX_VALUE) { return Integer.MAX_VALUE; } return (int)_timeout; }

可以看到核心规律:

  • isLastingCookie = false时,Cookie 的Max-Age被设为-1,即会话级(临时)Cookie,浏览器关闭即清除;
  • isLastingCookie = true时,Cookie 的存活时长与 Token 有效期保持一致;
  • 若 Token 设为永久有效(-1)或超出int上限,Cookie 存活时长取Integer.MAX_VALUE

最终这个值通过new SaCookie().setMaxAge(cookieTimeout)写入 Cookie(见 StpLogic.java)。

值得注意的是,Token 本体在服务端存储的有效期与 Cookie 存活时长是两套独立的机制:持久 Cookie 只是让浏览器“留着这张令牌”,令牌在服务端是否过期,仍由timeout决定。因此更安全的实践是让 Cookie 存活时长 ≤ Token 服务端有效期,Sa-Token 默认的策略正是二者保持一致。

三、登录时的四种重载写法

StpLogic提供了多种登录重载,方便在不同场景下快速指定登录细节(对应静态方法见 StpUtil.java):

// 1. 默认登录:使用全局配置(默认即“记住我”,有效期 30 天) StpUtil.login(10001); // 2. 指定设备类型(用于同端互斥登录等场景) StpUtil.login(10001, "PC"); // 3. 指定是否为“记住我” StpUtil.login(10001, true); // 持久 Cookie StpUtil.login(10001, false); // 临时 Cookie,关浏览器即失效 // 4. 指定 Token 有效期(单位:秒),7 天有效 StpUtil.login(10001, 60 * 60 * 24 * 7);

从 StpLogic.java 的实现可以看到,这些重载最终都会汇聚到login(id, SaLoginParameter):先把id与参数对象传入createLoginSession()创建会话,再把生成的 Token 注入当前客户端(Storage + Cookie + 可选响应头),也就是说登录行为的所有细节都由SaLoginParameter这一个参数模型统一承载

四、登录时指定 Token 有效期

除了指定是否「记住我」,你还可以在登录时指定一个精确的 Token 有效时长:

// 指定 token 有效期(单位:秒),如下所示 token 七天有效 StpUtil.login(10001, new SaLoginParameter().setTimeout(60 * 60 * 24 * 7));

这里的SaLoginParameter会以全局配置为默认值(见 SaLoginParameter.java 的setDefaultValues方法),你只需覆盖关心的字段即可,未指定的项自动沿用 SaTokenConfig 中的全局配置。

SaLoginParameter 全参数速查

SaLoginParameter是登录参数的完整 Model,覆盖了登录时的各种细节逻辑,常用参数如下:

StpUtil.login(10001, new SaLoginParameter() .setDeviceType("PC") // 此次登录的客户端设备类型,用于[同端互斥登录]时指定此次登录的设备类型 .setIsLastingCookie(true) // 是否为持久Cookie(临时Cookie在浏览器关闭时会自动删除,持久Cookie在重新打开后依然存在) .setTimeout(60 * 60 * 24 * 7) // 指定此次登录token的有效期, 单位:秒(如未指定,自动取全局配置的 timeout 值) .setActiveTimeout(60 * 60) // 指定此次登录token的最低活跃频率, 单位:秒(如未指定,则使用全局配置的 activeTimeout 值) .setToken("xxxx-xxxx-xxxx-xxxx") // 预定此次登录生成的Token .setIsWriteHeader(false) // 是否在登录后将 Token 写入到响应头 .setIsConcurrent(true) // 是否允许同一账号多地同时登录(false 时新登录挤掉旧登录) .setIsShare(true) // 多人登录同一账号时是否共用一个 token .setMaxLoginCount(-1) // 同一账号最大登录数量,-1 代表不限 );

各字段含义与取值说明(依据 SaLoginParameter.java 源码注释):

参数类型说明
deviceTypeString客户端设备类型(默认DEFAULT_LOGIN_DEVICE_TYPE),用于同端互斥登录
deviceIdString客户端设备 id
timeoutlongToken 有效期(秒),未指定取全局timeout(默认 30 天,-1永久有效)
activeTimeoutLongToken 最低活跃频率(秒),超过该时间未访问系统会被冻结,默认-1不限制
isConcurrentBoolean是否允许同一账号多地同时登录,false时新登录挤掉旧登录
isShareBoolean多人登录同一账号时是否共用同一个 token
maxLoginCountint同一账号最大登录数量,-1不限(仅在isConcurrent=true, isShare=false时生效)
maxTryTimesint创建 token 时的最高循环次数,用于保证唯一性(-1不循环尝试)
isLastingCookieBoolean是否为持久 Cookie,即「记住我」开关
isWriteHeaderBoolean是否在登录后将 Token 写入响应头
tokenString预定本次登录生成的 Token 值
extraDataMap扩展信息(仅在 JWT 模式下生效),可用setExtra(key, value)写入
rightNowCreateTokenSessionBoolean登录时是否立即创建 Token-Session
cookieSaCookieConfig本次登录的 Cookie 配置对象(domain、path、secure、httpOnly、sameSite 等),可通过setupCookieConfig(...)定制

写入响应头的注意事项

isWriteHeadertrue时,框架会把 Token 写入当前请求的响应头。从 StpLogic.java 的setTokenValueToResponseHeader()可以看到,框架除了写入 Token 本身,还会同时写入Access-Control-Expose-Headers响应头——否则跨域场景下前端 JS 将无法读取到该自定义响应头。这一细节在前后端分离开发时需要留意。

五、前后端分离模式下如何实现「记住我」

Cookie 虽好,却无法在前后端分离环境下使用——APP、小程序等客户端默认没有实现 Cookie 功能,因此任何基于 Cookie 的认证方案在纯前后端分离架构下都会失效。

好在这些客户端一般都提供了本地存储方案,我们可以用「前端手动控制 Token 生命周期」的方式达到同样的效果,核心思路是用本地持久存储模拟「持久 Cookie」、用会话级内存存储模拟「临时 Cookie」。

跨端框架(uni-app 等)示例

// 使用本地存储保存token,达到 [持久Cookie] 的效果(关掉应用再打开依然有效) uni.setStorageSync("satoken", "xxxx-xxxx-xxxx-xxxx-xxx"); // 使用 globalData 保存token,达到 [临时Cookie] 的效果(应用退出即失效) getApp().globalData.satoken = "xxxx-xxxx-xxxx-xxxx-xxx";

PC 浏览器前后端分离示例

// 使用 localStorage 保存token,达到 [持久Cookie] 的效果 localStorage.setItem("satoken", "xxxx-xxxx-xxxx-xxxx-xxx"); // 使用 sessionStorage 保存token,达到 [临时Cookie] 的效果(关闭浏览器标签即失效) sessionStorage.setItem("satoken", "xxxx-xxxx-xxxx-xxxx-xxx");

在这种模式下,前端的职责是:登录接口返回 Token 后,根据用户是否勾选「记住我」选择对应的存储介质保存;之后每次请求(拦截器/axios 请求头)携带该 Token 供服务端校验;退出登录或 Token 过期时再将其清除。后端则通过 Sa-Token 的 Token 读取机制从请求头或参数中获取凭证完成鉴权。

六、完整可运行的 Demo:RememberMeController

为了便于直接上手验证,仓库在sa-token-demo-case模块中提供了一个完整的控制器示例:RememberMeController.java,其中包含三种典型登录场景:

@RestController @RequestMapping("/RememberMe/") public class RememberMeController { // 记住我登录 ---- http://localhost:8081/RememberMe/doLogin?name=zhang&pwd=123456 @RequestMapping("doLogin") public SaResult doLogin(String name, String pwd) { if("zhang".equals(name) && "123456".equals(pwd)) { StpUtil.login(10001, true); return SaResult.ok("登录成功"); } return SaResult.error("登录失败"); } // 不记住我登录 ---- http://localhost:8081/RememberMe/doLogin2?name=zhang&pwd=123456 @RequestMapping("doLogin2") public SaResult doLogin2(String name, String pwd) { if("zhang".equals(name) && "123456".equals(pwd)) { StpUtil.login(10001, false); return SaResult.ok("登录成功"); } return SaResult.error("登录失败"); } // 七天免登录 ---- http://localhost:8081/RememberMe/doLogin3?name=zhang&pwd=123456 @RequestMapping("doLogin3") public SaResult doLogin3(String name, String pwd) { if("zhang".equals(name) && "123456".equals(pwd)) { StpUtil.login(10001, 60 * 60 * 24 * 7); return SaResult.ok("登录成功"); } return SaResult.error("登录失败"); } }

三个接口分别演示了「记住我登录」「不记住我登录」「指定 7 天有效期登录」,启动该模块后可直接通过浏览器访问对应 URL 验证效果,是理解本文全部内容的最佳实践入口。

七、小结与选型建议

「记住我」功能在 Sa-Token 中实现成本极低,关键在于理解它的两层设计:

  1. Cookie 层面isLastingCookie决定写入持久 Cookie 还是临时 Cookie,控制“浏览器关闭后 Token 是否还在”;
  2. 服务端层面timeout决定 Token 在服务端存储的有效期,控制“令牌本身何时过期”。

建议按业务场景选择:

  • 默认登录:直接StpUtil.login(id),即持久 Cookie + 30 天有效期,适合对会话时长要求宽松的场景;
  • 敏感操作StpUtil.login(id, false),临时 Cookie,关闭浏览器即失效,适合涉及资金、隐私等敏感功能;
  • 精细化控制:通过SaLoginParameter组合isLastingCookietimeoutactiveTimeoutisConcurrent等参数,按接口粒度定制每一次登录行为;
  • 前后端分离:放弃 Cookie 方案,前端根据「记住我」勾选状态选择 localStorage / sessionStorage 保存 Token,配合响应头或请求参数传递凭证。

相关参考:RememberMeController 示例、SaLoginParameter 参数模型、StpLogic 登录实现、SaTokenConfig 全局配置。

【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架,让鉴权变得简单、优雅!—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 8:06:16

2026年AI降重工具测评与写作效率提升指南

1. 项目概述:AI降重工具的崛起与写作压力缓解2026年的内容创作领域正经历一场静默革命。当我在深夜赶稿时,无意中发现自己的写作习惯已经彻底改变——不再反复纠结于"这句话会不会被判定为AI生成",而是专注于内容质量本身。这种转变…

作者头像 李华
网站建设 2026/9/14 8:05:35

SpringBoot+Vue点餐平台开发实战:从表设计到订单状态机

简介:以点餐平台网站作为主题的Java毕业设计项目,基于Spring Boot与Vue技术栈实现前后端分离的B/S架构,是一份包含源码、说明与数据库的完整工程资料。项目面向计算机专业学生,适用于毕业设计、课程设计及全栈开发练习&#xff0c…

作者头像 李华
网站建设 2026/9/14 8:02:45

Netty不是Java必修课,却是高并发架构分水岭:线程模型与实战避坑

做Java这么久,不管是在技术群还是面试现场,“要不要深入学Netty”这个问题我听了不下几十遍。有人觉得Netty就是搞网络编程的框架,业务开发根本碰不到;也有人一头扎进Netty源码,结果被Reactor模型和堆外内存折腾得怀疑…

作者头像 李华
网站建设 2026/9/14 8:02:35

工业视觉云边协同架构设计与落地实践:从固化困境到持续进化

前阵子跟一个做汽车零部件视觉检测的朋友吃饭,他说了一句话让我印象深刻:“我们那套视觉系统,验收那天就是它最好用的一天,之后每天都在走下坡路。”三年前上线的设备,当时节拍、准确率全部达标,可如今客户…

作者头像 李华
网站建设 2026/9/14 7:59:20

Rocky 9.4 下 ELK 日志分析系统部署实战与性能优化

1. 项目概述与整体方案设计1.1 为什么要在Rocky 9.4上搭ELK日志分析这件事,只要是跑业务的服务器,基本都绕不开。服务器一多,靠着tail -f逐个翻日志的日子就过不下去了。ELK这套组合——Elasticsearch负责存储和检索,Logstash负责…

作者头像 李华