最近有个粉丝问了一个问题,说他在SpringSecurity中使用JWT登出时获取不到当前用户,导致无法证明“我是那个想退出的人”,业务失败!经过大量研究我找到了原因,包括我自己在内的大多数人都犯过这个错误。Session会话之所以被称为Session会话是因为SpringSecurity的默认配置是有session的,所以当你登录的时候,这个Session会一直被服务器保存到你退出,只要保存这个Session,你的请求只要从ServletRequest中获取到当前的HttpSession进入服务器,然后根据HttpSession加载当前的SecurityContext,相关逻辑在SpringSecurity的默认过滤器SecurityContextPersistenceFilter中,有兴趣的可以看相关源码。默认情况下,SecurityContextPersistenceFilter的优先级高于退出过滤器LogoutFilter,所以可以保证在有Session会话的时候可以获取到当前用户。sessionlesssession使用JWT后,每个请求都必须携带BearerToken,并被特殊的过滤器拦截解析后,才能将用户认证信息保存在SecurityContext中。参考SpringSecurity实战干货教程中JwtAuthenticationFilter的Token认证实现。相关逻辑是://当token匹配时if(jwtToken.equals(accessToken)){//解析权限集合这里JSONArrayjsonArray=jsonObject.getJSONArray("roles");Listroles=jsonArray.toList(String.class);String[]roleArr=roles.toArray(newString[0]);Listauthorities=AuthorityUtils.createAuthorityList(roleArr);Useruser=newUser(username,"[PROTECTED]",authorities);//构建用户认证tokenUsernamePasswordAuthenticationTokenusernamePasswordAuthenticationToken=newUsernamePasswordAuthenticationToken(user,null,authorities);usernamePasswordAuthenticationToken.setDetails(newWebAuthenticationDetailsS??ource().buildDetails(request));//放入安全上下文中SecurityContextHolder.getContext()。setAuthentication(usernamePasswordAuthenticationToken);}else{//token不匹配if(log.isDebugEnabled()){log.debug("token:{}isnotinmatched",jwtToken);}thrownewBadCredentialsException("tokenisnotmatched");}为什么不能'tlogout获取当前用户,分析两种情况配置完本例用户认证信息的安全上下文后,我们回到问题本身,看看为什么使用JWT无法获取到当前的认证信息。在HttpSecurity中,同学是这样配置JwtAuthenticationFilter的顺序的:httpSecurity.addFilterBefore(jwtAuthenticationFilter,UsernamePasswordAuthenticationFilter.class)再来看SpringSecurity过滤器排序图:SpringSecurity过滤器排序是指LogoutFilter执行和退出时,JWT还没有被被JwtAuthenticationFilter拦截,当然无法获取到当前的认证上下文SecurityContext。解决方案解决方案是在执行LogoutFilter之前解析JWT,将认证成功的信息保存到SecurityContext中。我们可以这样配置:httpSecurity.addFilterBefore(jwtAuthenticationFilter,LogoutFilter.class)这样问题就迎刃而解了,只需要取消当前的JWT即可登出。本文转载自微信公众号“码农小胖哥”,可通过以下二维码关注。转载本文请联系码农小胖公众号。