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

Spring Boot实现AuthenticationManager时出现StackOverflowError问题求助

问题:使用Spring AuthenticationManager登录时出现StackOverflowError(递归循环)

我想用Spring内置的AuthenticationManager实现登录功能,代码按预期编写,但一直收到以下错误:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed: java.lang.StackOverflowError] with root cause

我知道程序陷入了递归循环,但不清楚具体原因。


我的User类

@Entity
@Table(name = "users")
@Data
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int userId;

    @Column(nullable = false, unique = true)
    private String username;

    @Column(nullable = false, unique = true)
    private String email;

    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    private String street;

    @Column(nullable = false)
    private int streetNo;

    @Column(nullable = false)
    private int zipCode;

    @Column(nullable = false)
    private String city;

    @ManyToMany // add fetchtypes later
    private Set<Role> roles;
}

我的LoginDTO

@Data
public class LoginDTO {
    private String username;
    private String password;
}

UserController中的登录方法

@PostMapping("/login")
public ResponseEntity<String> authenticateUser(@RequestBody LoginDTO loginDTO) {
    return userService.authenticateUser(loginDTO);
}

UserService中的方法(已自动装配所有所需依赖)

@Service
public class UserService {
    private final UserRepository userRepository;
    private final RoleRepository roleRepository;
    private final PasswordEncoder passwordEncoder;
    private final UserMapper userMapper;
    private final AuthenticationManager authenticationManager;

    @Autowired
    public UserService(
            UserRepository userRepository,
            RoleRepository roleRepository,
            PasswordEncoder passwordEncoder,
            UserMapper userMapper,
            AuthenticationManager authenticationManager) {
        this.userRepository = userRepository;
        this.roleRepository = roleRepository;
        this.passwordEncoder = passwordEncoder;
        this.userMapper = userMapper;
        this.authenticationManager = authenticationManager;
    }

    public ResponseEntity<String> authenticateUser (LoginDTO loginDTO) {
        try {
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(loginDTO.getUsername(), loginDTO.getPassword()));

            SecurityContextHolder.getContext().setAuthentication(authentication);
            return new ResponseEntity<>(" is successfully logged in.", HttpStatus.OK);

        } catch (AuthenticationException ex) {
            return new ResponseEntity<>("Authentication failed: " + ex.getMessage(), HttpStatus.UNAUTHORIZED);
        }
    }
}

我的SecurityConfig

我知道不应禁用CSRF,但为了先让下一步功能正常,暂时让Spring禁用了它。

@Configuration
public class SecurityConfig {

    @Bean
    public static PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
        return configuration.getAuthenticationManager();
    }

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .securityContext((context) -> context.requireExplicitSave(false))
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.ALWAYS))
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests((requests) -> requests
                .requestMatchers(
                        "/api/product/create",
                        "/api/user/logout"
                        ).authenticated()
                .requestMatchers(
                        "/api/user/register",
                        "/api/user/{id}",
                        "/api/category/**",
                        "/api/product/**",
                        "/api/user/login"
                ).permitAll());
    return http.build();
    }
}

我试过移除User类和LoginDTO的@Data注解,替换为allArgsConstructor和noArgsConstructor,但没有效果。在UserService中打印loginDTO可以正常输出,但问题似乎出在调用authenticationManager进行认证的步骤,我确认用户凭证是正确的。


问题原因与解决方案

核心原因

出现StackOverflowError是因为Spring Security找不到正确的UserDetailsService实现,导致AuthenticationManager在认证流程中陷入递归调用。默认情况下,AuthenticationManager依赖UserDetailsService加载用户信息,你的代码中缺少这个关键组件,引发循环。

解决步骤

  1. 实现UserDetailsService接口
    创建自定义实现类,从数据库加载用户并转换为Spring Security所需的UserDetails对象:
@Service
public class CustomUserDetailsService implements UserDetailsService {

    private final UserRepository userRepository;

    public CustomUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new UsernameNotFoundException("User not found with username: " + username));

        return org.springframework.security.core.userdetails.User.builder()
                .username(user.getUsername())
                .password(user.getPassword())
                .authorities(user.getRoles().stream()
                        .map(role -> new SimpleGrantedAuthority(role.getName()))
                        .collect(Collectors.toList()))
                .build();
    }
}

注意:确保Role类有getName()方法,返回标准格式的角色名(如"ROLE_USER")

  1. 确认密码编码一致性
    用户注册时必须使用SecurityConfig中定义的BCryptPasswordEncoder加密密码,否则认证时会因密码不匹配失败。

  2. 修复Role实体的递归问题
    如果Role类使用了@Data注解,添加@ToString(exclude = "users")排除与User的关联字段,避免加载角色时触发递归调用。

完成上述修改后,AuthenticationManager就能通过CustomUserDetailsService正确加载用户信息,完成认证流程,不会再出现StackOverflowError。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 17:46:03