当前位置: 首页 > news >正文

springboot+satoken实现刷新token(值变化)

欢迎来到我的博客,代码的世界里,每一行都是一个故事


在这里插入图片描述

🎏:你只管努力,剩下的交给时间

🏠 :小破站

springboot+satoken实现刷新token

    • satoken是什么?支持什么?
    • 为什么需要?
    • token一直不变存在的问题
      • 1. 安全风险增加
      • 2. 难以撤销 Token
      • 3. 权限滥用和过期信息的风险
      • 4. 缺乏会话管理
      • 5. 影响用户隐私
      • 6. 无法确保设备和网络变化
      • 7. 用户体验不佳
    • 逻辑+代码实现
      • 代码实现
    • 拦截器知识补充
      • 1. 注册顺序决定执行顺序
      • 2. 拦截器方法的执行顺序
      • 3. 优先级控制
      • 总结

satoken是什么?支持什么?

satoken官网

借用官网的一句话, Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证权限认证单点登录OAuth2.0分布式Session会话微服务网关鉴权 等一系列权限相关问题。
这里我们只说明刷新token,也就是前后端分离的场景中常见的一种方案。

为什么需要?

在satoken中存在两个token,一个是真正的token有效期,还有一个是活跃token,也就是说当活跃token过期的时候是不能访问服务的,需要调用相关方法解除。而当正真的token过期的时候就需要登录重新获取凭证。

所以其实根据上面的表述也是可以实现刷新token的
前端后端约定,当后端因为活跃token过期返回给前端响应的状态值,前端拦截并重新调用相关方法,这样也是可以实现刷新token的。

token一直不变存在的问题

Token 长期不变或过期时间过长会带来一系列安全和用户体验方面的问题。以下是一些主要的风险和潜在问题:

1. 安全风险增加

  • Token 被截获的风险:如果攻击者通过某种方式截获了用户的 Token,那么在 Token 长期不变或过期时间过长的情况下,攻击者可以持续使用该 Token 访问用户的账号,而用户不会意识到。这极大增加了账号被恶意使用的风险。
  • 无法控制 Token 失效:在 Token 长期有效的情况下,即使用户想要主动注销或更改密码,攻击者手中的旧 Token 仍然有效,导致安全威胁无法解除。
  • 无法防御会话劫持:当用户登录后 Token 长期不变,一旦发生会话劫持,攻击者可以一直利用该 Token 冒充用户进行操作,直到 Token 失效。

2. 难以撤销 Token

  • Token 黑名单机制的缺乏:当 Token 的有效期非常长时,后端很难立即撤销某个 Token。即使用户账号被禁用或者注销,旧的 Token 可能依然可以继续访问系统,直到 Token 到期。
  • 缺乏灵活性:Token 如果是长期有效的,即便用户被强制下线,无法简单地让旧 Token 失效,除非重新设计 Token 管理系统。

3. 权限滥用和过期信息的风险

  • 权限变化后无法及时更新:当 Token 长时间有效,而用户的权限发生了变化(例如角色升级、降级或权限被撤销),旧 Token 可能依然具有不该有的权限。这可能导致用户获得不适当的访问权限。
  • 过期数据风险:如果用户 Token 长期不更新,系统可能无法捕捉到最新的用户状态变更,比如权限、角色、信息等,导致系统提供了不正确的访问权限或内容。

4. 缺乏会话管理

  • 无法追踪用户的活跃度:长期有效的 Token 会让系统无法准确跟踪用户的登录会话。系统无法判断用户是否活跃,用户的最后访问时间也无法准确追踪。
  • 无法强制用户重新登录:如果 Token 过期时间过长,用户可能在极长时间内不需要重新登录,丧失了会话管理的能力。对于需要更高安全性的场景,强制用户定期登录是必要的。

5. 影响用户隐私

  • 隐私泄露风险增加:Token 长期有效可能使得用户的个人信息在长时间内暴露于潜在的攻击面,增加了隐私泄露的风险。如果用户长期未使用系统,Token 应该过期以保护用户隐私。
  • 设备共享中的风险:如果用户在共享设备上登录,而 Token 长期不失效,其他人可以轻松访问用户账户,特别是在用户忘记登出或清理浏览器时。

6. 无法确保设备和网络变化

  • IP地址、设备等环境因素没有变化:一些 Token 通常会包含用户设备、IP 地址等信息来防止 Token 被滥用。如果 Token 长期不变,那么即使用户的网络环境发生了变化,系统也无法感知。这将导致 Token 在不可信的环境中继续使用,增加了安全风险。

7. 用户体验不佳

  • 无法提供个性化内容更新:长期不变的 Token 可能导致系统无法捕捉用户状态的实时变化,从而影响个性化内容推荐或提示用户更新信息的能力。
  • 会话管理不灵活:如果用户希望在不同设备上管理会话(如在一个设备上登出时使另一个设备上的 Token 失效),长期不变的 Token 可能无法支持这种场景。

逻辑+代码实现

可以采用双拦截器实现,第一个拦截器是自定义的,而这个拦截器总是返回true,第二个拦截器使用satoken的拦截器做一些登录或者权限的认证。

在这里插入图片描述

代码实现

package fun.acowbo.config;import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.IdUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** @author <a href="https://acowbo.fun">acowbo</a>* @since 2024/8/27*/
@Slf4j
@Component
public class CustomInterceptor implements HandlerInterceptor{@Value("${sa-token.token-name}")private String tokenName;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {return true;}long tokenActivityTimeout = StpUtil.getTokenActivityTimeout();log.info("tokenActivityTimeout:{}", tokenActivityTimeout);long tokenTimeout = StpUtil.getTokenTimeout();if (tokenActivityTimeout > 0){response.setHeader(tokenName, StpUtil.getTokenValue());response.setHeader("Access-Control-Expose-Headers", tokenName);}if (tokenActivityTimeout < 0 && tokenTimeout > 0){// 首先要让token活跃StpUtil.updateLastActivityToNow();String loginId = (String) StpUtil.getLoginId();// 先退出,否则之前的token还能用StpUtil.logout(loginId);// 重新设置token,这里仅仅是为了安全,否则始终token是一个值StpUtil.login(loginId,new SaLoginModel().setToken(IdUtil.randomUUID()));// 请求头修改token的值,否则在第二个拦截器会报错,因为老的token已经失效了request.setAttribute(tokenName, StpUtil.getTokenValue());// 响应头设置值response.setHeader(tokenName, StpUtil.getTokenValue());response.setHeader("Access-Control-Expose-Headers", tokenName);}return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {}
}
package fun.acowbo.config;import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;
import java.util.Arrays;/*** description: sa-token权限配置类** @author <a href="https://acowbo.fun">acowbo</a>* @since 2024/6/7 13:59*/
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {@Value("${excludePath}")private String excludePath;@Resourceprivate CustomInterceptor customInterceptor;@Overridepublic void addCorsMappings(CorsRegistry registry) {// 允许所有路径registry.addMapping("/**")// 允许所有来源.allowedOrigins("*")// 允许的 HTTP 方法.allowedMethods("GET", "POST", "PUT", "DELETE")// 允许的请求头.allowedHeaders("*")// 允许发送 Cookie.allowCredentials(false)// 预检请求的缓存时间.maxAge(3600);}// 注册拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验。registry.addInterceptor(customInterceptor).addPathPatterns("/**").excludePathPatterns(Arrays.asList(excludePath.split(",")));registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin())).addPathPatterns("/**").excludePathPatterns(Arrays.asList(excludePath.split(",")));}
}

拦截器知识补充

在 Spring 框架中,如果定义了多个拦截器,它们的执行顺序是根据它们的注册顺序决定的。具体的执行顺序可以通过以下规则来理解:

1. 注册顺序决定执行顺序

在 Spring 中,拦截器是通过 WebMvcConfigurer 接口中的 addInterceptors 方法进行注册的。多个拦截器会按照它们注册的先后顺序进行调用。

  • 拦截器的执行顺序(进入请求时):按照注册顺序依次调用,先注册的拦截器会先执行。
  • 拦截器的执行顺序(响应时):响应返回时,拦截器的执行顺序与请求时相反,最后注册的拦截器会最先执行。

示例

@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new FirstInterceptor()).addPathPatterns("/**");registry.addInterceptor(new SecondInterceptor()).addPathPatterns("/**");}
}

在这个例子中:

  • 进入请求时FirstInterceptor -> SecondInterceptor
  • 返回响应时SecondInterceptor -> FirstInterceptor

2. 拦截器方法的执行顺序

拦截器的核心方法有以下三个,它们的调用顺序也需要注意:

  • preHandle: 在请求处理之前执行。
  • postHandle: 在请求处理完后(但在视图渲染之前)执行。
  • afterCompletion: 在视图渲染完成后执行。

当有多个拦截器时:

  • preHandle 方法按照拦截器的注册顺序执行。
  • postHandleafterCompletion 方法则按照相反顺序执行。

3. 优先级控制

如果需要更精确地控制拦截器的顺序,除了按注册顺序,还可以借助其他方法:

  • 排序接口:通过实现 Ordered 接口或使用 @Order 注解,可以为拦截器明确指定顺序。
  • 配置拦截器链的顺序:通过配置文件明确指定拦截器的顺序,也可以使用 InterceptorRegistry 的 API 动态调整顺序。

总结

  • 拦截器的执行顺序取决于它们的注册顺序,先注册的拦截器先处理请求,后注册的拦截器先处理响应。
  • 可以使用 @Order 注解或实现 Ordered 接口来精确控制多个拦截器的执行顺序。

相关文章:

  • STL之stackqueue篇(上)探索C++ STL中的Queue与Stack——构建数据处理的基础框架
  • django drf to_representation
  • NVIDIA Hopper 架构深入
  • 刷题学习日记 (1) - SWPUCTF
  • Python FFmpeg 安装使用教程
  • k8s 1.28.2 集群部署 ingress 1.11.1 包含 admission-webhook
  • 优化后的版本
  • 【Vue】vue2项目打包后部署刷新404,配置publicPath ./ 不生效问题
  • 正则表达式中的贪婪模式和非贪婪模式
  • 小程序原生-利用setData()对不同类型的数据进行增删改
  • 《微信小程序实战(4) · 地图导航功能》
  • 【每天学个新注解】Day 10 Lombok注解简解(九)—@Accessors
  • 第十三届蓝桥杯真题Java c组C.纸张尺寸(持续更新)
  • UNI-APP_iOS开发技巧之:跳转到TestFlight或者App Store
  • SQL第11课——使用子查询
  • download使用浅析
  • es6
  • exports和module.exports
  • QQ浏览器x5内核的兼容性问题
  • Travix是如何部署应用程序到Kubernetes上的
  • Vue2.0 实现互斥
  • WordPress 获取当前文章下的所有附件/获取指定ID文章的附件(图片、文件、视频)...
  • 阿里云购买磁盘后挂载
  • 从输入URL到页面加载发生了什么
  • 大数据与云计算学习:数据分析(二)
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 马上搞懂 GeoJSON
  • 七牛云 DV OV EV SSL 证书上线,限时折扣低至 6.75 折!
  • 容器服务kubernetes弹性伸缩高级用法
  • 使用 @font-face
  • 微信支付JSAPI,实测!终极方案
  • 原生Ajax
  • 分布式关系型数据库服务 DRDS 支持显示的 Prepare 及逻辑库锁功能等多项能力 ...
  • # centos7下FFmpeg环境部署记录
  • #pragma data_seg 共享数据区(转)
  • #我与Java虚拟机的故事#连载19:等我技术变强了,我会去看你的 ​
  • (02)Hive SQL编译成MapReduce任务的过程
  • (39)STM32——FLASH闪存
  • (day 2)JavaScript学习笔记(基础之变量、常量和注释)
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (一)项目实践-利用Appdesigner制作目标跟踪仿真软件
  • 、写入Shellcode到注册表上线
  • .NET 4.0中的泛型协变和反变
  • .NET MVC、 WebAPI、 WebService【ws】、NVVM、WCF、Remoting
  • .NET基础篇——反射的奥妙
  • .NET下的多线程编程—1-线程机制概述
  • .pop ----remove 删除
  • @Autowired注解的实现原理
  • @modelattribute注解用postman测试怎么传参_接口测试之问题挖掘
  • [bzoj 3534][Sdoi2014] 重建
  • [C++] 如何使用Visual Studio 2022 + QT6创建桌面应用
  • [C++基础]-初识模板
  • [Contest20180313]灵大会议
  • [CSDN首发]鱿鱼游戏的具体玩法详细介绍
  • [Electron]ipcMain.on和ipcMain.handle的区别