Sa-Token 名词解释:Token、Session、loginId 与登录鉴权策略的系统梳理
【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架,让鉴权变得简单、优雅!—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token
在 Sa-Token 中,"Token""Session""登录策略"这些词经常被混用,而绝大多数会话逻辑 bug 的根源正是对这些基础概念的理解偏差。本文以官方文档《Sa-Token 名词解释》(sa-token-doc-new/docs/more/noun-intro.md) 为核心骨架,结合 StpUtil.java、SaTokenConfig.java 等源码逐一澄清五种 Token、两种过期时间、三种 Session、账号标识、登录/注销策略与三种鉴权方式,帮助你在阅读文档、排查问题和提出 issue 之前先建立准确的术语体系。
一、几种 Token:按产生来源区分五种令牌
Sa-Token 官方文档首先强调:Token 并非只有一种,按产生来源可分为五类,混用名称是最常见的理解偏差来源。
| 名称 | 产生入口 | 常见别名 | 作用 |
|---|---|---|---|
| token(会话 Token) | StpUtil.login() | satoken、会话Token | 维护用户登录状态 |
| temp-token(临时 Token) | SaTempUtil.createToken() | 临时Token | 一次性接口防盗用、短时间资源访问 |
| Access-Token | OAuth2 模块 | 访问令牌、资源令牌 | OAuth2 客户端访问授权资源的身份凭证 |
| Refresh-Token | OAuth2 模块 | 刷新令牌 | 用于换取新的 Access-Token |
| Same-Token | SaSameUtil模块 | 同源令牌 | 子服务外网隔离场景下的内部调用鉴权 |
会话 Token(satoken)
即配置项tokenName默认值satoken所对应的令牌。从 StpUtil.java 可以看到,StpUtil.login(Object id)、StpUtil.login(Object id, String deviceType)、StpUtil.login(Object id, SaLoginParameter loginParameter)等重载统一委托给成员变量stpLogic(StpLogic对象)完成登录并生成会话 Token;SaTokenConfig.java 中tokenName的注释也印证了它同时是"Cookie 名称、提交 token 时参数的名称、存储 token 时的 key 前缀"。
临时 Token
SaTempUtil.java 是独立的"临时 token 验证模块",其类注释写明用途:"有效期很短的一种 token,一般用于一次性接口防盗用、短时间资源访问等业务场景"。核心 API 为:
// 为指定 value 创建一个临时 token(timeout 单位:秒,-1 代表永久有效) String token = SaTempUtil.createToken(value, timeout); // 解析 Token 获取 value Object value = SaTempUtil.parseToken(token);它与会话 Token 的关键区别在于:不产生登录态、不与loginId绑定,仅做"值 ↔ 令牌"的短期映射。
Access-Token 与 Refresh-Token
这两个令牌由 OAuth2 模块产生,其常量定义见 SaOAuth2Consts.java,配套的access_token/refresh_token校验逻辑分布在sa-token-plugin/sa-token-oauth2目录下(如 SaOAuth2DataResolver.java)。注意它与"会话 Token"的区别:OAuth2 的 Access-Token 授权主体是客户端(应用),而会话 Token 授权主体是登录用户。
Same-Token
SaSameUtil.java 是"Samed-Token 同源系统身份认证模块",类注释说明其目标是"解决同源系统互相调用时的身份认证校验,例如微服务网关请求转发鉴权、微服务 RPC 调用鉴权"。常用 API:
// 获取 Same-Token(不存在则立即创建) String sameToken = SaSameUtil.getToken(); // 校验一个 Same-Token 是否有效(无效抛异常) SaSameUtil.checkToken(token);其有效期由全局配置项sameTokenTimeout控制,默认 1 天,见 SaTokenConfig.java。
二、两种过期时间:timeout 与 active-timeout
文档把会话 Token 的过期机制归纳为两个独立参数:
- timeout:会话 Token 的长久有效期,即 Token 最长能用多久;
- active-timeout:会话 Token 的最低活跃频率,即 Token 必须隔多久至少访问一次系统,否则被冻结。
两者的官方配置形式(源自 token-timeout.md):
sa-token: # token 有效期(单位:秒),默认30天,-1代表永不过期 timeout: 2592000 # token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结 active-timeout: -1# properties 风格 sa-token.timeout=2592000 sa-token.active-timeout=-1源码侧这两个参数的定义与默认值见 SaTokenConfig.java:
/** token 有效期(单位:秒) 默认30天,-1 代表永久有效 */ private long timeout = 60 * 60 * 24 * 30; /** * token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结, * 默认-1 代表不限制,永不冻结(例如可以设置为 1800 代表 30 分钟内无操作就冻结) */ private long activeTimeout = -1;官方文档用银行储蓄卡来类比:timeout相当于卡的最长使用年限,到期卡被删除(Token 过期必须重新登录);active-timeout相当于最低活跃要求,长期不动则被冻结(Token 被冻结但不会被删除)。两者可单独配置也可同时配置,只要有一个过期 Token 就不可用。
与这两种过期时间直接相关的 API 都能在 StpUtil.java 中找到:
StpUtil.checkActiveTimeout():检查当前 Token 是否已被冻结,冻结则抛异常;StpUtil.updateLastActiveToNow():手动续签,将"最后操作时间"更新为当前时间戳;StpUtil.getTokenActiveTimeout()/StpUtil.getTokenTimeout():分别查询距离冻结还剩多久、Token 剩余有效时间(返回 -1 代表永久有效,-2 代表没有该值);StpUtil.renewTimeout(long timeout):重置 token 的 timeout 有效期。
此外,配置项autoRenew(默认true)控制框架是否在每次直接或间接调用getLoginId()时自动进行过期检查与续签,可设为false将续签完全交给开发者手动控制。
三、三种 Session:账号维度、令牌维度、自定义维度
Sa-Token 文档将 Session 分为三种,区别在于"按什么主键分配":
| Session 类型 | 分配主键 | 获取方式 |
|---|---|---|
| Account-Session(账号 Session) | 账号 id | StpUtil.getSession() |
| Token-Session(令牌 Session) | 每个 Token | StpUtil.getTokenSession() |
| Custom-Session(自定义 Session) | 任意指定的 SessionId | SaSessionCustomUtil.getSessionById(id) |
Account-Session
框架只在调用StpUtil.login(id)登录时才产生 Session,且 Session 分配给账号 id而非客户端——PC 端和 APP 端登录同一账号得到的是同一个 Session,天然支持多端数据同步。操作示例:
// 获取当前会话的 Account-Session SaSession session = StpUtil.getSession(); // 从 Account-Session 中读取、写入数据 session.get("name"); session.set("name", "张三");对应源码入口为 StpUtil.java 的getSession()、getSessionByLoginId(Object loginId)等 Account-Session 相关方法区。
Token-Session
当需要"每个客户端独立"的数据(典型场景:某端两小时无操作自动下线)时,把数据放进共享的 Account-Session 会导致多端互相"续命"。Token-Session 就是按每个 Token 独立分配的:
// 获取当前会话的 Token-Session SaSession session = StpUtil.getTokenSession(); session.set("name", "张三");不同设备即使登录同一账号,只要 token 不同,对应的 Token-Session 就不同。相关 API 见 StpUtil.java,包括getTokenSessionByToken(String tokenValue)与未登录也可使用的getAnonTokenSession()。另外配置项rightNowCreateTokenSession(默认false)决定 Token-Session 是登录时立即创建,还是首次调用getTokenSession()时惰性创建,见 SaTokenConfig.java。
Custom-Session
不依赖账号 id 或 token,以任意指定值作为 SessionId,可理解为通用的分布式缓存 Session:
// 获取指定 key 的 Custom-Session SaSession session = SaSessionCustomUtil.getSessionById("goods-10001"); session.set("name", "张三");对应实现位于 SaSessionCustomUtil.java。Custom-Session 的会话有效期默认取全局配置的timeout,创建后可用session.updateTimeout(1000)单独修改。
三者的完整模型与结构图解见官方 Session模型详解:三个客户端登录同一账号(不共享 token)时,指向同一个 Account-Session,但各自持有不同的 Token-Session;简而言之——Account-Session 以账号 id 为主,Token-Session 以 token 为主,Custom-Session 以特定 key 为主。
四、账号标识:loginId、device、loginType
文档把"标识一个登录会话"拆成三个正交维度:
- loginId:账号 id,用来区分不同账号,通过
StpUtil.login(id)指定。从 StpUtil.java 的注释看,建议类型为long | int | String; - device:登录设备类型,例如
PC、APP,通过StpUtil.login(id, deviceType)指定; - loginType:账号类型(账号体系标识),用来区分同一系统中的
User账号和Admin账号。
loginType 的机制在源码中非常直白:StpUtil是一个"空壳门面类",所有静态方法都转发给成员变量stpLogic,而其TYPE常量固定为"login":
public class StpUtil { /** 多账号体系下的类型标识 */ public static final String TYPE = "login"; /** 底层使用的 StpLogic 对象 */ public static StpLogic stpLogic = new StpLogic(TYPE); // 所有静态方法均为对 stpLogic 的转发 }由此推出多账号体系的做法:新建一个StpUserUtil,把TYPE改为"user"即可得到一套与StpUtil完全隔离的登录/鉴权体系;也可以不复制类,直接声明new StpLogic("user")(Kit 模式)。完整方案(含注解鉴权的type属性、注解合并、同端多登录的 tokenName 隔离等)见 多账号认证文档。
注意 device 在 API 中实际以deviceType命名:SaLoginParameter.java 中字段名为deviceType,并额外提供deviceId(设备 id)供设备锁等场景使用;旧的setDevice()/getDevice()已标记@Deprecated,应改用setDeviceType()。
五、几种登录策略:从文档概念到配置项
文档列出的七种登录策略,在 Sa-Token 中分别对应明确的配置项或独立模块。以下对照表以 SaTokenConfig.java 中的字段注释为依据:
| 文档中的策略 | 含义 | 对应实现/配置 |
|---|---|---|
| 单地登录(单端登录) | 同一时间只能在一处登录,新登录挤掉旧登录 | isConcurrent: false |
| 多地登录(多端登录) | 不同地方可同时登录,新旧共存 | isConcurrent: true(默认) |
| 同端互斥登录 | 同类型设备单地点、不同类型设备可共存(参考 QQ 的登录模式) | isConcurrent: true+ 登录时指定device,顶替范围由replacedRange控制 |
| 限量登录 | 限制账号登录设备总数,超量后自动清退一个旧登录 | maxLoginCount(默认 12)+overflowLogoutMode |
| 记住我模式 | 设备重启后仍保持登录状态 | isLastingCookie: true(持久 Cookie) |
| 单点登录 | 进入多个系统只需登录一次 | SSO 模块(独立插件/文档体系) |
| 同端多登录 | 一个终端同时登录多个账号 | 多账号体系 + 重写 tokenName 防覆盖 |
逐项说明:
- 单地/多地登录:
isConcurrent字段注释为"是否允许同一账号多地同时登录(为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)",默认true。isConcurrent=false时还可以用replacedLoginExitMode(OLD_DEVICE旧设备下线 /NEW_DEVICE新设备登录失败)决定新旧设备谁放弃会话,用replacedRange(CURR_DEVICE_TYPE/ALL_DEVICE_TYPE)决定顶替范围。互斥登录的典型实践见 互斥登录文档。 - 限量登录:
maxLoginCount(同一账号最大登录数量,-1 不限)只在isConcurrent=true, isShare=false时生效;溢出时以何种方式清退旧会话由overflowLogoutMode决定(LOGOUT注销下线、KICKOUT踢人下线、REPLACED顶人下线)。 - 记住我:
isLastingCookie(默认true)决定 Cookie 是持久的还是临时的。StpUtil.login(id, isLastingCookie)还支持按单次登录动态指定。实现细节见 记住我模式文档。 - 单点登录:由独立 SSO 方案实现,概念与部署见 SSO 文档。
- 同端多登录:即"一个终端同时登录多个账号"。默认两套账号体系的 token 都写在名为
satoken的载体里会互相覆盖,解决方式是重写stpLogic.splicingKeyTokenName()让不同体系使用不同 token 名称,详见 多账号认证文档 第 8 节。
登录参数的两种方式:全局配置 与 按次覆盖
以上所有登录行为都可以"按次登录"覆盖,这正是loginId/device之外的第三个重要类——SaLoginParameter.java。它的无参构造器会以全局SaTokenConfig为默认值初始化,未指定的项自动继承全局配置:
// 全局配置(yml) // sa-token: // is-concurrent: true // max-login-count: 12 // is-lasting-cookie: true // 按次覆盖:此次登录仅 PC 端、有效期七天、不共享 token StpUtil.login(10001, new SaLoginParameter() .setDeviceType("PC") .setTimeout(60 * 60 * 24 * 7) .setIsShare(false));SaLoginParameter可覆盖的字段包括deviceType、deviceId、timeout、activeTimeout、isConcurrent、isShare、maxLoginCount、isLastingCookie、replacedRange、overflowLogoutMode、rightNowCreateTokenSession以及 Cookie 配置等,且每个字段均提供 setter 链式调用,便于登录接口中按业务动态组装登录行为。
六、几种注销策略
文档列出四种注销策略,其核心差异在"注销影响半径":
| 注销策略 | 含义 |
|---|---|
| 单端注销 | 只在调用登录的一端注销 |
| 全端注销 | 一端注销,全端下线 |
| 同端注销 | 发起注销后同类型设备端一起下线,不同设备类型不受影响 |
| 单点注销 | 一个系统注销,所有系统一起下线(SSO 场景) |
源码层面的基础抽象是枚举 SaLogoutRange.java:
public enum SaLogoutRange { /** token 范围:只注销提供的 token 指向的会话 */ TOKEN, /** 账号范围:注销 token 指向的 loginId 会话 */ ACCOUNT }全局默认值由配置项logoutRange指定(默认TOKEN,即单端注销),见 SaTokenConfig.java。"同端注销"则通过在注销时传入deviceType参数实现——StpUtil.java 提供了按loginId + deviceType维度操作的logout(Object loginId, String deviceType)、kickout(Object loginId, String deviceType)、replaced(Object loginId, String deviceType)等重载,注释中均说明"填 null 代表覆盖该账号的所有设备类型"。
另外要注意"注销、踢人、顶人"三种下线方式的语义差异(均定义在 StpUtil.java):
logout:主动注销,对端再次访问提示未登录;kickout:踢人下线,对端再次访问抛NotLoginException(场景值=-5);replaced:顶人下线,对端再次访问抛NotLoginException(场景值=-4),用于"新登录挤掉旧登录"场景。
"单点注销"属于 SSO 体系的注销扩展,见 SSO 注销文档。
七、几种鉴权方式:代码、注解、路由拦截
文档把鉴权落地方式归纳为三种,源码中三者齐备:
1、代码鉴权
在代码里直接调用StpUtil.checkXxx相关 API,最灵活、可任意嵌套条件:
// 校验当前会话是否已登录,未登录抛 NotLoginException StpUtil.checkLogin(); // 校验当前账号是否拥有指定角色/权限 StpUtil.checkRole("admin"); StpUtil.checkPermission("article:add"); // 需要自定义逻辑时也可先 isXxx 判断再自行抛错 if(StpUtil.hasRole("vip")) { ... }这些方法集中在 StpUtil.java 的"角色认证操作"与"权限认证操作"两个分区中。
2、注解鉴权
在方法或类上添加@SaCheckXxx注解。注解定义位于sa-token-core的annotation包下,如 SaCheckLogin.java、SaCheckRole、SaCheckPermission、SaCheckSafe等,每个注解配有独立的 handler(annotation/handler目录下的SaCheckLoginHandler等)。多账号体系下可通过注解的type属性指定校验哪套账号:
// 校验的是自定义 StpUserUtil 体系(type="user")的登录态 @SaCheckLogin(type = StpUserUtil.TYPE) @RequestMapping("info") public String info() { return "查询用户信息"; }3、路由拦截鉴权
在全局过滤器或拦截器里通过SaRouter.match()拦截路由做集中鉴权,是"按 URL 白名单/黑名单"管控的常用形态:
// 在拦截器中:匹配一批路由,未登录一律拦截 SaRouter.match("/admin/**").check(r -> StpUtil.checkRole("admin")); // 混合多账号体系示例(来自多账号认证文档) SaRouter.match("/art/getInfo").check(r -> StpUtil.checkLogin()); SaRouter.match("/art/getInfo").check(r -> StpUserUtil.checkLogin());路由匹配器实现见 SaRouter.java,它与SaFilter(Servlet 过滤器)及 Spring/Reactor/Solon 等平台的拦截器 starter 配合使用。
八、小结:一张表建立 Sa-Token 术语坐标系
| 概念族 | 成员 | 一句话区分 |
|---|---|---|
| Token | token / temp-token / Access-Token / Refresh-Token / Same-Token | 按产生模块区分:登录模块、临时验证模块、OAuth2 模块、同源认证模块 |
| 过期时间 | timeout / active-timeout | 前者管"最长活多久",后者管"多久不活跃就冻结" |
| Session | Account-Session / Token-Session / Custom-Session | 按账号 id / 按 token / 按自定义 key 分配 |
| 账号标识 | loginId / device / loginType | 哪个账号 / 哪种设备 / 哪套账号体系 |
| 登录策略 | 单地、多地、同端互斥、限量、记住我、单点、同端多登录 | 由 isConcurrent、replacedRange、maxLoginCount、isLastingCookie、SSO 模块、tokenName 隔离组合实现 |
| 注销策略 | 单端 / 全端 / 同端 / 单点注销 | 由 logoutRange 与 deviceType 维度控制,SSO 注销覆盖多系统 |
| 鉴权方式 | 代码 / 注解 / 路由拦截 | checkXxxAPI、@SaCheckXxx、SaRouter.match() |
以上每个条目均可在当前仓库中定位到实现:登录与鉴权 API 见 StpUtil.java(核心逻辑在StpLogic),全局配置见 SaTokenConfig.java,按次登录参数见 SaLoginParameter.java。遇到"Token 过期""Session 不同步""新旧设备互踢"类问题时,建议先回到本表的坐标系确认自己讨论的到底是哪一种 Token、哪一层 Session、哪个维度的注销策略,再对照 登录认证文档 与 Token有效期详解 定位配置项,可避免绝大多数因概念混淆导致的误判。
【免费下载链接】Sa-Token✨ 开源、免费、一站式 Java 权限认证框架,让鉴权变得简单、优雅!—— 登录认证、权限认证、分布式 Session 会话、微服务网关鉴权、SSO 单点登录、OAuth2.0 统一认证、jwt 集成、API Key 秘钥授权、API 参数签名项目地址: https://gitcode.com/GitHub_Trending/sa/Sa-Token
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考