Spring Security中用JWT退出登录时遇到的坑
作者:码农小胖哥 发布时间:2022-05-19 10:30:28
最近有个粉丝提了个问题,说他在Spring Security中用JWT做退出登录的时无法获取当前用户,导致无法证明“我就是要退出的那个我”,业务失败!经过我一番排查找到了原因,而且这个错误包括我自己的大部分人都犯过。
Session会话
之所以要说Session会话,是因为Spring Security默认配置就是有会话的,所以当你登录以后Session就会由服务端保持直到你退出登录。只要Session保持住,你的请求只要进入服务器就可以从 ServletRequest 中获取到当前的 HttpSession ,然后会根据 HttpSession 来加载当前的 SecurityContext 。相关的逻辑在Spring Security默认的过滤器 SecurityContextPersistenceFilter 中,有兴趣可以看相关的源码。
而且默认情况下 SecurityContextPersistenceFilter 的优先级是高于退出过滤器 LogoutFilter 的,所以能够保证有Session会话的情况下退出一定能够获取当前用户。
无Session会话
使用了JWT后,每次请求都要携带 Bearer Token 并且被专门的过滤器拦截解析之后才能将用户认证信息保存到 SecurityContext 中去。参考Spring Security实战干货教程中的Token认证实现 JwtAuthenticationFilter ,相关逻辑为:
// 当token匹配
if (jwtToken.equals(accessToken)) {
// 解析 权限集合 这里
JSONArray jsonArray = jsonObject.getJSONArray("roles");
List<String> roles = jsonArray.toList(String.class);
String[] roleArr = roles.toArray(new String[0]);
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(roleArr);
User user = new User(username, "[PROTECTED]", authorities);
// 构建用户认证token
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user, null, authorities);
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 放入安全上下文中
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
} else {
// token 不匹配
if (log.isDebugEnabled()){
log.debug("token : {} is not in matched", jwtToken);
}
throw new BadCredentialsException("token is not matched");
}
为什么退出登录无法获取当前用户
分析了两种情况下用户认证信息的安全上下文配置后,我们回到问题的本身。来看看为什么用JWT会出现无法获取当前认证信息的原因。在 HttpSecurity 中,那位同学是这样配置 JwtAuthenticationFilter 的顺序的:
httpSecurity.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
我们再看看 Spring Security 过滤器排序图:
也就说LogoutFilter执行退出的时候,JWT还没有被 JwtAuthenticationFilter 拦截,当然无法获取当前认证上下文 SecurityContext 。
解决方法
解决方法就是必须在 LogoutFilter 执行前去解析JWT并将成功认证的信息存到 SecurityContext 。我们可以这样配置:
httpSecurity.addFilterBefore(jwtAuthenticationFilter, LogoutFilter.class)
这样问题就解决了,你只要实现把当前JWT作废掉就退出登录了。
来源:https://blog.didispace.com/spring-security-jwt-logout-wrong-config


猜你喜欢
- ⛳️ 基本类型做形式参数(零散参数的数据接收)1、基本数据类型要求前台页面的表单输入框的name属性值与对应控制器方法中的形式参数名称与类型
- 1 引言在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchro
- 简介在实现登录功能时,一般为了安全都会设置验证码登录,为了防止某个用户用特定的程序暴力破解方式进行不断的尝试登录。常见验证码分为图片验证码和
- 本文实例为大家分享了Android实现圆形云标签效果展示的具体代码,供大家参考,具体内容如下下面是实现的效果图:这个适合用于选择 用户的一些
- 内存对齐的基本原则:结构(struct/class)的内置类型数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员的起始位置
- 内存模型Flink可以使用堆内和堆外内存,内存模型如图所示:flink使用内存划分为堆内内存和堆外内存。按照用途可以划分为task所用内存,
- • 创建目录和文件1、通过Path类的Combine方法可以合并路径。string activeDir = @"C:\myDir&
- Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。 1.方法声明时使
- 一、前言二、案例需求1.编写login.html登录页面,username&password两个输入框2.使用Druid数据库连接池
- 概述JavaScript是目前web开发中不可缺少的脚本语言,js不需要编译即可运行,运行在客户端,需要通过浏览器来解析执行JavaScri
- 一、首先编写一个工具类Hello:public class Hello { public static void say(Str
- 需要的Maven<!--redis--> <dependency&g
- 目录常用APIgeoaddgeoposgeodistgeoradiusbymembergeohash在外卖软件中的附近的美食店铺、外卖小哥的
- 实现InsertOrUpdate功能需求最近在项目开发中遇到这样一个需求:每天需要对相同的数据(也有可能是不同的)进行两次入库操作,数据不存
- 一、包装类概述Java有8种基本数据类型:整型(byte、short、int、long)、浮点型(float、double)、布尔型bool
- 本文实现Unity调用手机摄像,拍摄,然后识别二维码,显示二维码的内容。需要导入一个zxing.unity.dll文件,现在这个脚本的识别数
- 目录1、备份原数据库File文件2、数据库升级XML编写 updateXml.xml3、创建XML解析器3.1 对应工具类 DomUtils
- 前言在有些业务场景中,系统对于响应时间有一定的要求,而一个方法里面同步执行的业务逻辑太多势必会影响响应速度,带来不好的用户体验。比如登录时记
- 在java中,static是一个修饰符,用于修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能;被static关键
- 本文实例讲述了C#处理Access中事务的方法。分享给大家供大家参考。具体如下:Access不能像SQL server一样直接执行多条语句,