sa-token过期机制梳理
1 基本配置
sa-token 配置类
@AutoConfiguration
public class SaTokenConfiguration {
/**
* 生成Token实现
*/
@Bean
public StpLogic getStpLogicJwt() {
return new StpLogicJwtForSimple();
}
/**
* 权限接口实现(使用bean注入方便用户替换)
*/
@Bean
public StpInterface stpInterface() {
return new PlusSaStpInterface();
}
/**
* 自定义token dao层存储
* 这里使用 redis 实现
*/
@Bean
public SaTokenDao saTokenDao() {
return new PlusSaTokenDao();
}
}
application.yaml
sa-token:
# token有效期 设为一天 (必定过期) 单位: 秒
timeout: 86400
# 多端不同 token 有效期 可查看 LoginHelper.loginByDevice 方法自定义
# token最低活跃时间 (指定时间无操作就过期) 单位: 秒
active-timeout: 1800
2 登录过程
核心源码
// StpLogic.login(Object id, SaLoginModel loginModel)
// createLoginSession(Object id, SaLoginModel loginModel)
// saveTokenToIdMapping(tokenValue, id, loginModel.getTimeout());
// 1) 设置 token 信息
getSaTokenDao().set(splicingKeyTokenValue(tokenValue), String.valueOf(loginId), timeout);
// 2) 设置 超时时间
if(isOpenCheckActiveTimeout()) {
setLastActiveToNow(tokenValue, loginModel.getActiveTimeout(), loginModel.getTimeoutOrGlobalConfig());
}
生成 login 信息,这里使用 redis 实现,redis 的 key 设置了超时时间
# xxxx 为 token
key=Authorization:login:token:xxxxx
# value 为 用户id信息
value=sys_user:{userId}
同时设置了一个 last-active 时间
# xxxx 为 token
key=Authorization:login:last-active:xxxxx
# value 为 上一次检查时间的 毫秒时间戳,比如1690878257097
value={last-time}
3 检查过程
核心源码
// 拦截器中添加 StpLogic.checkLogin()
// StpLogic.checkLogin()
// StpLogic.getLoginId()
// 3、查找此 token 对应的 loginId,如果找不到则抛出:token 无效
// 这里会从 redis 中获取,由于登录时即设置了超时时间,所以token到达了超时时间必然会失效!!!
String loginId = getLoginIdNotHandle(tokenValue);
// getLoginIdNotHandle(tokenValue) 实现 -> getSaTokenDao().get(splicingKeyTokenValue(tokenValue));
if(SaFoxUtil.isEmpty(loginId)) {
throw NotLoginException.newInstance(loginType, INVALID_TOKEN, INVALID_TOKEN_MESSAGE, tokenValue).setCode(SaErrorCode.CODE_11012);
}
// ......
// 7、检查此 token 的最后活跃时间是否已经超过了 active-timeout 的限制,如果是则代表其已被冻结,需要抛出:token 已被冻结
if(isOpenCheckActiveTimeout()) {
checkActiveTimeout(tokenValue);
// ------ 至此,loginId 已经是一个合法的值,代表当前会话是一个正常的登录状态了
// 8、如果配置了自动续签功能, 则: 更新这个 token 的最后活跃时间 (注意此处的续签是在续 active-timeout,而非 timeout)
if(getConfigOrGlobal().getAutoRenew()) {
updateLastActiveToNow(tokenValue);
}
}
也即是会先从 redis 中获取 Authorization:login:token:xxxxx
对应的值,由于该 key 的 timeout 在登录时已经确定了,所以超时后必定获取不到 value 从而报错
获取到了 value 值,那么会接着取出 Authorization:login:last-active:xxxxx
上次登录时的时间戳,与 配置中的 active-timeout
进行比较
- 如果超过了
active-timeout
,直接抛出错误 - 如果未超过,则刷新
Authorization:login:last-active:xxxxx
的值