Spring Security中利用JWT退出登录大部分人都写错了配置

举报
码农小胖哥 发表于 2022/04/14 00:51:43 2022/04/14
【摘要】 最近有个粉丝提了个问题,说他在Spring Security中用JWT做退出登录的时无法获取当前用户,导致无法证明“我就是要退出的那个我”,业务失败!经过我一番排查找到了原因,而且这个错误包括我自己的大部分人都犯过。 Session会话 之所以要说Session会话,是因为Spring Security默认配置就是有会话的...

c3855b5552e767a831f782ccefdc5601.gif

最近有个粉丝提了个问题,说他在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,相关逻辑为:


   
  1. // 当token匹配         
  2. if (jwtToken.equals(accessToken)) {
  3.                   // 解析 权限集合  这里
  4.                 JSONArray jsonArray = jsonObject.getJSONArray("roles");
  5.                 List<String> roles = jsonArray.toList(String.class);
  6.                 String[] roleArr = roles.toArray(new String[0]);
  7.                 List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(roleArr);
  8.                 User user = new User(username, "[PROTECTED]", authorities);
  9.                 // 构建用户认证token
  10.                 UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(user, null, authorities);
  11.                 usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
  12.                 // 放入安全上下文中
  13.                 SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
  14.             } else {
  15.                 // token 不匹配
  16.                 if (log.isDebugEnabled()){
  17.                     log.debug("token : {}  is  not in matched", jwtToken);
  18.                 }
  19.                 throw new BadCredentialsException("token is not matched");
  20.             }

为什么退出登录无法获取当前用户

分析了两种情况下用户认证信息的安全上下文配置后,我们回到问题的本身。来看看为什么用JWT会出现无法获取当前认证信息的原因。在HttpSecurity中,那位同学是这样配置JwtAuthenticationFilter的顺序的:

httpSecurity.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
  

我们再看看Spring Security过滤器排序图:

4738e2e93220894f900e0d9373e3aa6a.png

Spring Security过滤器排序

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

解决方法

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

httpSecurity.addFilterBefore(jwtAuthenticationFilter, LogoutFilter.class)
  

这样问题就解决了,你只要实现把当前JWT作废掉就退出登录了。

JWT应该保存在哪里?

2021-10-11

ef3c70869259a25f327366367a5ee8d6.png

从Java 9 到 Java 17之Java 11

2021-09-29

df5a124d2b6a902b8501a7d9db456dd6.png

cc124d0c73f36600a03eb1e6a1880207.gif

文章来源: felord.blog.csdn.net,作者:码农小胖哥,版权归原作者所有,如需转载,请联系作者。

原文链接:felord.blog.csdn.net/article/details/120775731

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。