You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security设置maximumSessions=1不生效问题求助

解决Spring Security单会话限制不生效的问题

核心问题分析

你的单会话限制失效主要源于三个关键问题:

  1. 自定义登录流程未触发会话认证策略:手动实现的login方法没有调用Spring Security的SessionAuthenticationStrategy,这是控制并发会话的核心校验步骤。
  2. SessionRegistry未关联Spring Session:默认的SessionRegistryImpl无法感知Spring Session JDBC存储的会话,需要专门的实现类对接。
  3. 重复配置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中移除失效会话,保证数据一致性。

验证修改效果

  1. 用同一用户在不同浏览器登录,第二次登录会被拒绝(因maxSessionsPreventsLogin(true))。
  2. 查看Spring Session的JDBC表(默认spring_session),同一用户只会存在一条活跃会话记录。
  3. 用户注销或会话超时后,对应会话记录会自动从表中移除,SessionRegistry同步更新。

内容的提问来源于stack exchange,提问作者xxlali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 06:32:53