最近在开发登录接口时,使用了SaToken权限认证框架来实现。sa-token是一个JavaWeb轻量级权限认证框架,其API调用非常简单,以此来记录下使用过程中遇到的问题
目录
[1. 前端存储方式(数据存在哪里)](#1. 前端存储方式(数据存在哪里))
[2. 身份认证方式(服务器怎么确认 "你是你")](#2. 身份认证方式(服务器怎么确认 “你是你”))
[模式 1:普通模式(Cookie 认证模式)](#模式 1:普通模式(Cookie 认证模式))
[模式 2:localStorage 模式(Token 认证模式)](#模式 2:localStorage 模式(Token 认证模式))
[2、将token写入 HTTP 响应头](#2、将token写入 HTTP 响应头)
鉴权的两种模式
核心概念
1. 前端存储方式(数据存在哪里)
- Cookie :浏览器内置的存储机制,容量小,会自动随每次 HTTP 请求发送到服务器,可设置过期时间、域名、路径限制。
- localStorage :HTML5 新增的前端本地存储,容量大,不会自动随请求发送到服务器,需手动通过代码读取并携带,数据永久有效(除非手动删除)。
2. 身份认证方式(服务器怎么确认 "你是你")
- Cookie 认证:传统方案,服务器直接把用户身份信息(或加密后的会话 ID)写入 Cookie,后续请求浏览器自动带 Cookie,服务器验证即可。
- Token 认证 :现代方案(如 JWT、OAuth2.0),用户登录后服务器生成一个临时 "令牌"(Token)返回给前端,前端存起来,后续请求需手动携带 Token(如请求头 Authorization),服务器验证 Token 有效性。
模式 1:普通模式(Cookie 认证模式)
本质是「用 Cookie 作为存储载体 + Cookie 作为认证载体」的传统方案,流程如下:
- 用户输入账号密码登录;
- 服务器验证通过后,生成 Session ID (会话 ID,代表当前登录状态),通过
Set-Cookie响应头把 Session ID 写入浏览器 Cookie; - 后续用户访问其他页面 / 接口时,浏览器自动把 Cookie 里的 Session ID 带到服务器;
- 服务器通过 Session ID 查找对应的用户会话,确认身份后返回数据。
关键特点:
- 无需前端手动处理存储和携带,浏览器自动完成;
- 依赖 Cookie 的同源策略,安全性有基础保障(可设置
HttpOnly防止 JS 读取,Secure仅 HTTPS 传输,SameSite防止 CSRF 攻击); - 缺点:Cookie 容量小,无法存大量数据;跨域请求时 Cookie 携带复杂(需配置 CORS 的
credentials);分布式系统中 Session 共享麻烦(需 Redis 等存储 Session)。
模式 2:localStorage 模式(Token 认证模式)
本质是「用 localStorage 作为存储载体 + Token 作为认证载体」的现代方案,流程如下:
- 用户登录后,服务器验证通过,生成 Token(如 JWT Token,包含用户 ID、过期时间等加密信息),直接返回给前端(通常在响应体里);
- 前端通过代码把 Token 存入
localStorage(也可存 sessionStorage,区别是 sessionStorage 关闭标签页就失效); - 后续请求接口时,前端手动从
localStorage读取 Token,通过请求头(如Authorization: Bearer <Token>)携带到服务器; - 服务器解析并验证 Token(无需查数据库 / Redis,JWT 可自包含信息),确认身份后返回数据。
关键特点:
- 前端需手动处理 Token 的存储和携带(写代码实现);
- 不依赖 Cookie,跨域请求更简单(无需处理 Cookie 跨域问题);
- 容量大(localStorage 5MB),可存额外用户信息;
- 缺点:localStorage 易被 XSS 攻击(恶意脚本可读取),需配合前端防 XSS(如输入过滤、CSP 策略);Token 过期需手动处理(如刷新 Token)。
下面介绍SaToken中常见的常用的两个方法,对其内部的源码进行简单的阅读和理解,目的是为了理解SaToken内部生成和存储token的方式,加深使用其他方法时,知道其中一些属性的来源
1.login()

login一共完成了两个步骤:
1、创建token,绑定会话

这段代码是Sa-Token 框架中负责创建登录会话、生成登录 Token 的核心方法 ,属于 Sa-Token 登录认证流程的核心实现逻辑(通常位于 StpLogic 类中)。其主要作用是完成用户登录时的 Token 生成、会话绑定、状态存储等一系列操作,下面逐行拆解其功能:
作用
接收用户唯一标识(id)和登录配置(loginModel),完成登录会话的创建:生成可用 Token、绑定用户会话、存储 Token 与用户的映射关系,并触发登录事件,最终返回生成的登录 Token。
功能解析
-
参数校验
this.checkLoginArgs(id, loginModel);校验登录参数的合法性(如id不能为空、loginModel不能为 null 等),确保登录请求参数有效。 -
加载配置并构建登录模型
javaSaTokenConfig config = this.getConfigOrGlobal(); loginModel.build(config);- 获取 Sa-Token 的配置(优先当前
StpLogic的配置,无则用全局配置); - 根据配置补全
loginModel的默认值(如超时时间、设备类型、Token 签名标签等),确保登录模型参数完整。
- 获取 Sa-Token 的配置(优先当前
-
生成可用的 Token
String tokenValue = this.distUsableToken(id, loginModel);生成一个全局唯一的、可用的 Token 值(支持 UUID、JWT、自定义生成规则等),确保 Token 不重复、符合配置规则。 -
创建 / 获取用户会话并更新超时时间
javaSaSession session = this.getSessionByLoginId(id, true); session.updateMinTimeout(loginModel.getTimeout());- 根据用户
id获取会话(若不存在则创建,true表示自动创建); - 更新会话的最小超时时间(确保会话有效期不低于本次登录的超时配置)。
- 根据用户
-
记录 Token 签名信息到会话
javaTokenSign tokenSign = new TokenSign(tokenValue, loginModel.getDeviceOrDefault(), loginModel.getTokenSignTag()); session.addTokenSign(tokenSign);将本次登录的 Token、设备类型、签名标签等信息封装为
TokenSign对象,存入用户会话中(用于支持多端登录、设备管理、踢人下线等功能)。 -
保存 Token 与用户的映射关系
this.saveTokenToIdMapping(tokenValue, id, loginModel.getTimeout());将 Token 与用户id绑定并存储(默认存储在 Redis 或内存中),同时设置 Token 的过期时间,用于后续认证时通过 Token 快速查询用户身份。 -
处理活跃超时(可选)
javaif (this.isOpenCheckActiveTimeout()) { this.setLastActiveToNow(tokenValue, loginModel.getActiveTimeout(), loginModel.getTimeoutOrGlobalConfig()); }若开启了 "活跃超时" 功能(即用户长时间不操作自动过期),则记录 Token 的最后活跃时间为当前时间,用于后续超时判断。
-
触发登录事件 、
SaTokenEventCenter.doLogin(this.loginType, id, tokenValue, loginModel);发布登录成功事件,允许开发者通过监听事件实现自定义逻辑(如记录登录日志、发送登录通知等)。 -
处理最大登录数限制
javaif (config.getMaxLoginCount() != -1) { this.logoutByMaxLoginCount(id, session, (String)null, config.getMaxLoginCount()); }若配置了 "同一账号最大登录数"(非 -1 表示开启限制),则检查当前用户的登录会话数,若超过限制则自动踢掉最早的登录会话(实现 "单端登录 / 多端登录限制")。
-
返回生成的 Token
return tokenValue;将最终生成的登录 Token 返回,供前端存储并用于后续请求认证。
2、将token写入 HTTP 响应头

作用
这段代码是Sa-Token 框架中负责将生成的 Token 分发到不同存储位置的辅助方法 ,核心作用是把登录成功后的 Token 传递给前端(或存储到上下文),确保前端能获取并在后续请求中携带 Token 完成认证。通常在 createLoginSession 生成 Token 后调用,是登录流程中 "Token 交付" 环节的关键实现。
功能解析
-
Token 非空校验
if (!SaFoxUtil.isEmpty(tokenValue)) {通过 Sa-Token 内置工具类SaFoxUtil判断 Token 值非空,避免空值处理。 -
Token 存入上下文存储
this.setTokenValueToStorage(tokenValue);将 Token 存入当前请求的上下文存储(如ThreadLocal),方便后续请求处理中快速获取 Token(例如权限校验时直接从上下文读取),属于框架内部的 Token 临时存储。 -
Token 写入 Cookie(可选)
javaif (this.getConfigOrGlobal().getIsReadCookie()) { this.setTokenValueToCookie(tokenValue, loginModel.getCookieTimeout()); }- 检查全局配置是否开启 "从 Cookie 读取 Token"(
getIsReadCookie),若开启则将 Token 写入浏览器 Cookie; - 同时设置 Cookie 的超时时间(从
loginModel中获取),确保 Cookie 有效期与 Token 一致,前端后续请求会自动携带 Cookie 中的 Token。
- 检查全局配置是否开启 "从 Cookie 读取 Token"(
-
Token 写入响应头(可选)
javaif (loginModel.getIsWriteHeaderOrGlobalConfig()) { this.setTokenValueToResponseHeader(tokenValue); }- 检查
loginModel或全局配置是否开启 "Token 写入响应头"; - 若开启则将 Token 写入 HTTP 响应头(默认头名通常为
satoken或Authorization),前端可从响应头中提取 Token(例如 Ajax 请求获取响应头后存储到 localStorage)。
- 检查
2、checkLogin()
代码执行流程
内部调用StpLogic的getLoginId 类,这段代码是Sa-Token 框架中获取当前登录用户 ID 的核心方法 (通常位于 StpLogic 类),负责从请求中解析 Token、验证 Token 有效性,并返回对应的登录用户 ID;若 Token 无效 / 过期 / 被踢等,会抛出针对性的未登录异常。

Token获取方式
优先级如下:
1.上下文获取(如果调用了login(),Token会被注册到上下文)
2.query参数获取
3.header中获取(鉴权时常用,一般header中的属性为Authorization)
4.cookie中获取(调用login()后,会自动在响应头中加入set-cookie字段,请求时会自动携带cookie字段,因此可以读取,注意,重启服务后,cookie存储的token会失效)

有了以上两个函数,其实鉴权就很容易了,首先在登陆成功后,调用login()函数,生成token返回给前端;后续前端携带该token来访问后端接口时,后端通过过滤器或拦截器进行拦截,调用checkLogin()方法进行鉴权即可。下面是鉴权两种写法的区别
1、注册过滤器(内部已经实现了Filter类,所以直接使用SaServletFilter 即可)
java
@Configuration
public class SaTokenConfigure {
@Autowired
private UserService userService;
/**
* 注册 [Sa-Token全局过滤器]
*/
@Bean
public SaServletFilter getSaServletFilter() {
return new SaServletFilter()
// 指定 拦截路由 与 放行路由
.addExclude("/see/**")
.addExclude("/shareConfig/queryList")
.addExclude("/api/user/**")
.addInclude("/**")
// 认证函数:每次请求都会执行
.setAuth(obj -> {
System.out.println("---------- 进入Sa-Token全局认证 -----------");
// 从请求头获取token
StpUtil.checkLogin();
})
// 异常处理函数:每次认证函数发生异常时执行此函数
.setError(e -> {
System.out.println("---------- 认证失败 -----------");
System.out.println(e.getMessage());
// 设置响应状态码为401未授权
SaHolder.getResponse()
.setStatus(401);
return SaResult.error(e.getMessage());
});
}
}
2、注册全局拦截器
java
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验。
registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
.addPathPatterns("/**")
.excludePathPatterns("/user/doLogin");
}
}
这是个人的学习笔记,有不清楚的地方见谅,佬们勿喷~