Servlet的doFilter与Spring Security监听器中Session ID不一致问题求助
嗨,我来帮你搞定这个Session ID不一致的问题!
你遇到的这个问题,核心原因大概率是在单例的LoggedUserListener里直接注入了请求作用域的对象(HttpSession/HttpServletRequest)。毕竟@Service默认是单例Bean,而HttpSession和HttpServletRequest是跟着每个请求走的,直接注入的话,拿到的只会是第一次请求时的实例,根本不是当前认证请求对应的那个,Session ID自然对不上。
另外还有一种可能:Spring Security默认会做Session Fixation防护(防止会话固定攻击),比如认证成功后自动创建新Session。如果你的Filter在认证流程之前执行,拿到的是旧Session,而监听器是认证成功后触发的,此时已经切换到新Session,ID肯定不一样。
1. 动态获取当前请求的Session(最推荐)
别再直接注入HttpSession或HttpServletRequest了,改用RequestContextHolder动态获取当前请求的上下文,这样每次触发事件时拿到的都是当前请求的真实Session:
@Service @Transactional public class LoggedUserListener implements ApplicationListener<AuthenticationSuccessEvent> { @Override public void onApplicationEvent(AuthenticationSuccessEvent event) { // 先拿到当前请求的上下文 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes == null) { // 非Web请求场景直接返回就行 return; } HttpServletRequest request = attributes.getRequest(); HttpSession session = request.getSession(); // 这里的Session就是当前请求对应的那个 // 取出认证成功的用户信息 Authentication authentication = event.getAuthentication(); Object principal = authentication.getPrincipal(); // 假设你的用户实体是User,强转后存入Session if (principal instanceof User) { session.setAttribute("loggedUser", principal); } } }
2. 检查Session Fixation配置
如果你的Spring Security配置里开了sessionFixation().newSession(),认证成功后会创建新Session,这时候Filter(如果在认证前执行)拿到的旧Session和监听器的新Session ID肯定不同。你可以调整这个配置:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .sessionManagement(session -> session .sessionFixation() .migrateSession() // 把旧Session的属性迁移到新Session,既保留安全防护又不丢数据 // 或者用none()关闭这个特性,但不推荐,毕竟是安全防护 ); return http.build(); } }
3. 调整自定义Filter的执行顺序
如果你的自定义Filter是在Spring Security的认证Filter之前跑的,那时候还没完成认证,Session可能还没被Spring Security处理。可以把Filter的顺序调到Spring Security认证Filter之后:
@Configuration public class FilterConfig { @Bean public FilterRegistrationBean<YourCustomFilter> yourCustomFilter() { FilterRegistrationBean<YourCustomFilter> registrationBean = new FilterRegistrationBean<>(); registrationBean.setFilter(new YourCustomFilter()); // 设置在Spring Security核心Filter之后执行 registrationBean.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE - 10); return registrationBean; } }
先试试第一种方案,这是最常见的问题根源。如果还不行,再检查Session Fixation配置和Filter的执行顺序,应该就能解决Session ID不一致的问题了。
内容的提问来源于stack exchange,提问作者AJN

