Sa-Token 记住我(Remember Me)模式:原理、实现与前后端分离方案
【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架,让鉴权变得简单、优雅!—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token
本文围绕 Sa-Token 的“[记住我] 模式”展开,讲解其背后的 Cookie 生命周期原理、
StpUtil.login()的完整参数体系,以及 APP、小程序、PC 前后端分离环境下的等价实现方案。读完本文,你将掌握如何用一行代码控制登录后的浏览器会话存留,并为不同客户端定制 Token 有效期、持久 Cookie 与响应头写入等细节行为。
什么是 [记住我] 模式
大多数网站的登录界面都会提供一个[记住我]复选框:勾选它登录后,即使关闭浏览器再重新打开网站,用户依然处于登录状态,无须重新输入密码;不勾选时,一旦关闭浏览器,会话即随之失效。
其本质是浏览器Cookie 生命周期的两种形态:
- 临时 Cookie:有效期仅限本次会话(浏览器进程),关闭浏览器窗口即被清除;
- 持久 Cookie:有效期是一个具体的时间段,只要未到期,即使关闭浏览器 Cookie 也依然保留。
Sa-Token 正是利用这一特性,把 Token 的存留策略与“是否记住我”绑定:勾选时写入持久 Cookie,不勾选时写入临时 Cookie。
在 Sa-Token 中实现:默认就是 [记住我]
Sa-Token 的登录授权默认就是[记住我]模式。因此,如果要实现“非记住我”(关闭浏览器后需重新登录),只需要在登录时显式传入false:
// 设置登录账号id为10001,第二个参数指定是否为[记住我] // 当此值为 false 后,关闭浏览器再次打开需要重新登录 StpUtil.login(10001, false);该重载在 StpUtil.java 中定义,isLastingCookie为true时即“记住我”,false时关闭浏览器需重新登录。从源码可以推断,它最终等价于:
StpUtil.login(10001, new SaLoginParameter().setIsLastingCookie(false));仓库内 sa-token-demo-case 提供了可直接运行对照的示例接口:
// 记住我登录 ---- http://localhost:8081/RememberMe/doLogin?name=zhang&pwd=123456 StpUtil.login(10001, true); // 不记住我登录 ---- http://localhost:8081/RememberMe/doLogin2?name=zhang&pwd=123456 StpUtil.login(10001, false);实现原理:Token 写入 Cookie 的底层机制
登录后,Sa-Token 会把 Token 写入当前会话的 Cookie,核心逻辑位于 StpLogic.java 的setTokenValueToCookie()方法:
SaCookie cookie = new SaCookie() .setName(getTokenName()) .setValue(tokenValue) .setMaxAge(cookieTimeout) // Cookie 存活时间(单位:秒) ... SaHolder.getResponse().addCookie(cookie);关键在于cookieTimeout的取值,它由 SaLoginParameter.getCookieTimeout() 计算得出:
public int getCookieTimeout() { if (!Boolean.TRUE.equals(getIsLastingCookie())) { return -1; // 非持久 Cookie:-1,浏览器关闭即失效 } long _timeout = getTimeout(); if (_timeout == SaTokenDao.NEVER_EXPIRE || _timeout > Integer.MAX_VALUE) { return Integer.MAX_VALUE; // 永不过期或超大值,取 Integer 上限 } return (int)_timeout; // 持久 Cookie:存活时间 = Token 有效期 }于是两种模式在浏览器侧表现为:
| 登录方式 | 写入的 Cookie | Cookie Max-Age | 关闭浏览器后 |
|---|---|---|---|
StpUtil.login(10001, true) | 持久 Cookie | 等于 Token 有效期(秒) | Token 仍保留,无需重新登录 |
StpUtil.login(10001, false) | 临时 Cookie | -1(内存 Cookie) | Token 随浏览器进程消失,会话失效 |
这也解释了为什么“不记住我”模式下,服务端 Token 数据其实并未立即删除——只是浏览器丢失了 Token 凭据,下次无法再携带它发起认证。
全局配置:isLastingCookie 默认值为 true
“默认就是记住我”的根源在于全局配置。在 SaTokenConfig.java 中:
/** * 是否为持久Cookie(临时Cookie在浏览器关闭时会自动删除,持久Cookie在重新打开后依然存在) */ private Boolean isLastingCookie = true;如果希望整个项目默认走“非记住我”,可以在配置文件中将sa-token.is-lasting-cookie设为false,随后未显式指定该参数的login()调用都会自动使用非持久 Cookie。
前后端分离:Cookie 失效了怎么办?
一个自然的疑问是:Cookie 方案依赖浏览器会话机制,那在APP、小程序等前后端分离环境下是否无效?
答案是肯定的——任何基于 Cookie 的认证方案在前后端分离环境下都会失效,因为这些客户端默认没有实现 Cookie 功能。不过,它们通常都提供了替代的本地存储机制,代价是Token 的生命周期需要由前端手动控制。
以经典跨端框架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");提示:
localStorage的数据不会随标签页/窗口关闭而清除,对应“记住我”;sessionStorage的生命周期与浏览器会话绑定,对应“非记住我”。仓库中 sa-token-demo-remember-me 提供了一个完整的 Vue3 + Vite 前端工程 + Spring Boot 后端的“记住我”演示:后端 UserLoginController.java 通过StpUtil.login(10001, remember)接收前端复选框状态,前端工程在page_project/目录下,可参考其登录、状态校验与注销的完整交互。
登录时指定 Token 有效期
“记住我”只是二选一的开关,实践中往往还需要精确控制 Token 的有效时长。登录时可以用SaLoginParameter指定一个具体时间(单位:秒):
// 示例1:指定 token 有效期(单位:秒),如下所示 token 七天有效 StpUtil.login(10001, new SaLoginParameter().setTimeout(60 * 60 * 24 * 7));当传入timeout后,持久 Cookie 的 Max-Age 也会同步等于该时长(见上文getCookieTimeout()逻辑),即“记住我 + 七天有效”。
对应地,StpUtil.login(Object id, long timeout)这个便捷重载可直接使用:
// 七天免登录 ---- http://localhost:8081/RememberMe/doLogin3?name=zhang&pwd=123456 StpUtil.login(10001, 60 * 60 * 24 * 7);上述接口均可在 RememberMeController.java 中看到完整上下文。
SaLoginParameter:登录参数的完整清单
SaLoginParameter是登录时的参数 Model,除“记住我”与有效期外,还决定登录过程中的各种行为。该类的全部字段与含义定义于 SaLoginParameter.java,核心参数整理如下:
| 参数 | 含义 | 备注/默认来源 |
|---|---|---|
deviceType | 此次登录的客户端设备类型 | 用于[同端互斥登录]时指定设备端,默认取全局配置 |
deviceId | 此次登录的客户端设备 id | 用于设备维度会话管理 |
timeout | 此次登录 token 有效期(秒) | 未指定时自动取全局timeout值 |
activeTimeout | 此次登录 token 最低活跃频率(秒) | 未指定时使用全局activeTimeout |
isConcurrent | 是否允许同一账号多地同时登录 | true一起登录,false新登录挤掉旧登录 |
isShare | 多人登录同一账号时是否共用一个 token | true共用,false每次新建 |
maxLoginCount | 同一账号最大登录数量 | -1不限,仅在isConcurrent=true, isShare=false时有意义 |
maxTryTimes | 创建 token 时的最高循环尝试次数 | 用于保证 token 唯一性,-1不循环直接使用 |
isLastingCookie | 是否为持久 Cookie | true记住我,false临时 Cookie |
isWriteHeader | 是否在登录后将 Token 写入响应头 | 前后端分离场景常用 |
token | 预定本次登录生成的 Token 值 | 需自行保证唯一性 |
rightNowCreateTokenSession | 登录时是否立即创建 Token-Session | true立即创建,false首次调用getTokenSession()时创建 |
replacedLoginExitMode/replacedRange | 顶人下线时的策略与范围 | 仅isConcurrent=false时生效 |
overflowLogoutMode | 溢出maxLoginCount时的下线方式 | LOGOUT注销 /KICKOUT踢人 /REPLACED顶人 |
extraData | 扩展信息 | 只在 jwt 模式下生效 |
cookie | Cookie 配置对象 | 可单独覆盖 domain、path、secure、httpOnly、sameSite 等 |
一个覆盖主要参数的完整示例:
StpUtil.login(10001, new SaLoginParameter() .setDeviceType("PC") // 此次登录的客户端设备类型,用于[同端互斥登录]时指定此次登录的设备类型 .setIsLastingCookie(true) // 是否为持久Cookie(临时Cookie在浏览器关闭时会自动删除,持久Cookie在重新打开后依然存在) .setTimeout(60 * 60 * 24 * 7) // 指定此次登录token的有效期,单位:秒(如未指定,自动取全局配置的 timeout 值) .setToken("xxxx-xxxx-xxxx-xxxx") // 预定此次登录生成的Token .setIsWriteHeader(false) // 是否在登录后将 Token 写入到响应头 );从源码结构看,SaLoginParameter的构造器会调用setDefaultValues(SaManager.getConfig()),将全局配置一次性填充为默认值(SaLoginParameter.java),因此你只需显式设置需要覆盖的字段,未指定的行为自动跟随全局配置,这正是“按次登录精细控制、不污染全局”的设计思路。
实践建议与注意事项
- 区分两层过期:服务端 Token 过期时间与浏览器 Cookie 存活时间并非同一概念。持久 Cookie 只是让 Token 凭据“不丢”,真正限制登录状态的是 Token 有效期;非持久 Cookie 则是“凭据丢失”导致被动下线。理解这一点有助于排查“明明没过期却掉线”的问题。
- 前后端分离时前端负责“存”:由于不依赖 Cookie,Token 的持久与否由前端存储方案(localStorage / sessionStorage / 小程序本地存储)决定,服务端只需保证 Token 本身的有效期配置正确。
- 响应头透传:在 APP 等场景可将
isWriteHeader设为true,登录后从响应头读取 Token,再由前端按需求自行存储,这与 SaTokenConfig 中isReadHeader、isReadCookie的读写配置相互配合,构成完整的双端 Token 传递闭环。 - 安全提示:为持久 Cookie 设置合理的有效期(而非永不过期),并配合 Token 活跃频率(
activeTimeout)实现“长时间无操作自动失效”,可在便利性与安全性之间取得平衡。
至此,从“勾选记住我”到“指定七天有效期”,再到前后端分离环境下的存储策略,Sa-Token 的会话存留机制已全部打通——正如官方文档所言:Remember me, it's too easy!
【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架,让鉴权变得简单、优雅!—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考