Spring Security中用JWT退出登录时遇到的坑


Posted in Java/Android onOctober 16, 2021

最近有个粉丝提了个问题,说他在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 过滤器排序图:

Spring Security中用JWT退出登录时遇到的坑

也就说LogoutFilter执行退出的时候,JWT还没有被 JwtAuthenticationFilter 拦截,当然无法获取当前认证上下文 SecurityContext 。

解决方法

解决方法就是必须在 LogoutFilter 执行前去解析JWT并将成功认证的信息存到 SecurityContext 。我们可以这样配置:

httpSecurity.addFilterBefore(jwtAuthenticationFilter, LogoutFilter.class)
这样问题就解决了,你只要实现把当前JWT作废掉就退出登录了。

到此这篇关于Spring Security中用JWT退出登录时遇到的坑的文章就介绍到这了,更多相关Spring Security JWT退出登录内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Java/Android 相关文章推荐
总结一些Java常用的加密算法
Jun 11 Java/Android
SpringCloud Alibaba项目实战之nacos-server服务搭建过程
Jun 21 Java/Android
Java并发编程之详解CyclicBarrier线程同步
Jun 23 Java/Android
springboot拦截器无法注入redisTemplate的解决方法
Jun 27 Java/Android
Jackson 反序列化时实现大小写不敏感设置
Jun 29 Java/Android
SSM项目使用拦截器实现登录验证功能
Jan 22 Java/Android
SpringBoot2零基础到精通之数据与页面响应
Mar 22 Java/Android
Android自定义双向滑动控件
Apr 19 Java/Android
Java对文件的读写操作方法
Apr 29 Java/Android
Java服务调用RestTemplate与HttpClient的使用详解
Jun 21 Java/Android
Spring Cloud OpenFeign模版化客户端
Jun 25 Java/Android
HttpClient实现文件上传功能
Aug 14 Java/Android
Java实现房屋出租系统详解
Oct 05 #Java/Android
Java Spring 控制反转(IOC)容器详解
Java spring定时任务详解
JAVA API 实用类 String详解
Oct 05 #Java/Android
SpringCloud之@FeignClient()注解的使用方式
Sep 25 #Java/Android
springboot中rabbitmq实现消息可靠性机制详解
Sep 25 #Java/Android
Spring Cloud 中@FeignClient注解中的contextId属性详解
Sep 25 #Java/Android
You might like
php smarty的预保留变量总结
2008/12/04 PHP
PHP使用redis实现统计缓存mysql压力的方法
2015/11/14 PHP
ThinkPHP模板循环输出Volist标签用法实例详解
2016/03/23 PHP
PHP常用函数总结(180多个)
2016/12/25 PHP
PHP合并数组函数array_merge用法分析
2017/02/17 PHP
php封装一个异常的处理类
2017/06/08 PHP
php使用socket调用http和smtp协议实例小结
2019/07/26 PHP
JavaScript实现向OL列表内动态添加LI元素的方法
2015/03/21 Javascript
javascript实现通过表格绘制颜色填充矩形的方法
2015/04/21 Javascript
node.js中 stream使用教程
2016/08/28 Javascript
input获取焦点时底部菜单被顶上来问题的解决办法
2017/01/24 Javascript
nodejs acl的用户权限管理详解
2018/03/14 NodeJs
通过webpack引入第三方库的方法
2018/07/20 Javascript
一文读懂ES7中的javascript修饰器
2019/05/06 Javascript
Vue Element UI + OSS实现上传文件功能
2019/07/31 Javascript
Vue的编码技巧与规范使用详解
2019/08/28 Javascript
Node对CommonJS的模块规范
2019/11/06 Javascript
基于jQuery拖拽事件的封装
2020/11/29 jQuery
[38:39]完美世界DOTA2联赛循环赛 IO vs GXR BO2第二场 11.04
2020/11/05 DOTA
[43:58]DOTA2-DPC中国联赛定级赛 LBZS vs SAG BO3第一场 1月8日
2021/03/11 DOTA
浅谈Python爬取网页的编码处理
2016/11/04 Python
Python字符编码与函数的基本使用方法
2017/09/30 Python
使用Python操作excel文件的实例代码
2017/10/15 Python
Python3实现取图片中特定的像素替换指定的颜色示例
2019/01/24 Python
seek引发的python文件读写的问题及解决
2019/07/26 Python
如何利用python正则表达式匹配版本信息
2020/12/09 Python
如何获取某个日期是当月的最后一天
2013/12/05 面试题
托管代码(Managed Code)和非托管代码(Unmanaged Code)有什么区别
2014/09/29 面试题
EntityManager都有哪些方法
2013/11/01 面试题
高级文秘工作总结的自我评价
2013/09/28 职场文书
建房协议书
2014/04/11 职场文书
服务宗旨标语
2014/07/01 职场文书
预备党员群众路线教育实践活动思想汇报2014
2014/10/25 职场文书
工程技术负责人岗位职责
2015/04/13 职场文书
导游词之丹东鸭绿江
2019/10/24 职场文书
Redis持久化与主从复制的实践
2021/04/27 Redis