Spring Security设置maximumSessions=1不生效问题求助
解决Spring Security单会话限制不生效的问题
核心问题分析
你的单会话限制失效主要源于三个关键问题:
- 自定义登录流程未触发会话认证策略:手动实现的login方法没有调用Spring Security的
SessionAuthenticationStrategy,这是控制并发会话的核心校验步骤。 - SessionRegistry未关联Spring Session:默认的
SessionRegistryImpl无法感知Spring Session JDBC存储的会话,需要专门的实现类对接。 - 重复配置SessionAuthenticationStrategy:
maximumSessions()已经会自动配置会话控制策略,手动再次设置会导致逻辑冲突。
具体修改步骤
1. 替换SessionRegistry实现类
因为使用Spring Session JDBC存储,需改用SpringSessionBackedSessionRegistry让SessionRegistry能读取到Spring Session中的会话数据:
@Bean public SessionRegistry sessionRegistry(JdbcIndexedSessionRepository sessionRepository) { return new SpringSessionBackedSessionRegistry<>(sessionRepository); }
2. 移除重复的SessionAuthenticationStrategy配置
删除sessionAuthenticationStrategy()Bean,以及sessionManagement中手动设置的会话认证策略。maximumSessions()会自动创建并配置对应的ConcurrentSessionControlAuthenticationStrategy,手动设置会覆盖默认逻辑。
修改后的sessionManagement方法:
private void sessionManagement(SessionManagementConfigurer<HttpSecurity> session) { session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .maximumSessions(1) .maxSessionsPreventsLogin(true) .sessionRegistry(sessionRegistry()); session.sessionFixation().newSession(); // 移除该行:session.sessionAuthenticationStrategy(sessionAuthenticationStrategy()); }
3. 调整自定义登录逻辑,触发会话校验
在login方法中调用SessionAuthenticationStrategy的onAuthentication方法,由Spring Security自动处理会话数量校验,同时移除手动注册会话的代码:
// 注入所需依赖 private final SessionAuthenticationStrategy sessionAuthenticationStrategy; private final SecurityContextRepository securityContextRepository; private final AuthenticationManager authenticationManager; @Autowired public YourLoginService(SessionAuthenticationStrategy sessionAuthenticationStrategy, SecurityContextRepository securityContextRepository, AuthenticationManager authenticationManager) { this.sessionAuthenticationStrategy = sessionAuthenticationStrategy; this.securityContextRepository = securityContextRepository; this.authenticationManager = authenticationManager; } public void login(UserLoginDTO loginDTO, HttpServletRequest request, HttpServletResponse response) { try { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(loginDTO.email(), loginDTO.password()) ); // 触发会话认证策略,校验并发会话数量 sessionAuthenticationStrategy.onAuthentication(authentication, request, response); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(authentication); securityContextRepository.saveContext(securityContext, request, response); // 移除手动注册会话代码:sessionRegistry.registerNewSession(request.getSession().getId(), authentication.getPrincipal()); } catch (BadCredentialsException e) { log.warn("login attempt with wrong credentials from: " + loginDTO.email()); throw new RuntimeException("Wrong Credentials"); } catch (SessionAuthenticationException e) { log.warn("login attempt exceeds max sessions for user: " + loginDTO.email()); throw new RuntimeException("当前用户已登录,无法重复登录"); } }
4. 保留HttpSessionEventPublisher配置
你的HttpSessionEventPublisher Bean配置正确,它能监听会话销毁事件,及时从SessionRegistry中移除失效会话,保证数据一致性。
验证修改效果
- 用同一用户在不同浏览器登录,第二次登录会被拒绝(因
maxSessionsPreventsLogin(true))。 - 查看Spring Session的JDBC表(默认
spring_session),同一用户只会存在一条活跃会话记录。 - 用户注销或会话超时后,对应会话记录会自动从表中移除,SessionRegistry同步更新。
内容的提问来源于stack exchange,提问作者xxlali
相关产品推荐
相关产品推荐

