AuthenticationManager Bean 定义中出现无法解析的循环引用的 StackOverflowError

问题描述 投票:0回答:1

我在 Spring Boot 应用程序中遇到 StackOverflowError。该问题似乎与 AuthenticationManager bean 的定义有关。我在 AuthenticationService 类中使用 AuthenticationManager bean,并在 SecurityConfig 类中定义它。然而,这似乎导致了循环依赖问题。

以下是相关代码片段:

package com.mehmetkaradana.java_api.service;

import com.mehmetkaradana.java_api.Repository.UserRepository;
import com.mehmetkaradana.java_api.model.User;
import com.mehmetkaradana.java_api.security.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class AuthenticationService {

@Autowired
private UserRepository userRepository;

@Autowired
@Lazy
private PasswordEncoder passwordEncoder;

@Autowired
private JwtUtil jwtUtil;

@Autowired
@Lazy
private AuthenticationManager authenticationManager;

public String authenticate(String username, String password) {
    System.out.println("Attempting authentication for user: " + username);
    authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
    return jwtUtil.generateToken(username);
}

public User saveUser(User user) {
    user.setPassword(passwordEncoder.encode(user.getPassword()));
    return userRepository.save(user);
}

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

}

还有

package com.mehmetkaradana.java_api.config;

import com.mehmetkaradana.java_api.security.JwtRequestFilter;
import com.mehmetkaradana.java_api.service.AuthenticationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.authentication.AuthenticationManager;
import     org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import static org.springframework.security.config.Customizer.withDefaults;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

private final AuthenticationService authenticationService;
private final JwtRequestFilter jwtRequestFilter;

@Autowired
public SecurityConfig(AuthenticationService authenticationService, JwtRequestFilter jwtRequestFilter) {
    this.authenticationService = authenticationService;
    this.jwtRequestFilter = jwtRequestFilter;
}

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(requests -> requests
                    .requestMatchers("/register", "/login").permitAll()
                    .anyRequest().authenticated()
            )
            .sessionManagement(session -> session
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            .addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
            .cors(withDefaults()); // Ensure CORS is configured if needed

    return http.build();
}

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

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

}

收到错误:

java.lang.StackOverflowError
at java.base/java.lang.reflect.Method.invoke(Method.java:561) ~[na:na]
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354) ~    [spring-aop-6.1.11.jar:6.1.11]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:216) ~[spring-aop-6.1.11.jar:6.1.11]

... 引起:org.springframework.beans.BeanInstantiationException:无法实例化[org.springframework.security.authentication.AuthenticationManager]:工厂方法“authenticationManager”抛出异常并显示消息:创建名为“securityConfig”的bean时出错:请求的bean当前位于创建:是否存在无法解析的循环引用? ...

问题:如何解决这个问题?尽管在 AuthenticationManager 和 PasswordEncoder bean 上使用 @Lazy 注释,我仍然遇到循环依赖错误。关于如何解决这个问题有什么建议或建议吗?

java spring-boot spring-security spring-security-config
1个回答
0
投票

stackoverflow的原因是这段代码:

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

如果你查看

AuthenticationConfiguration
类的内部,你会看到
AuthenticationConfiguration
尝试获取
AuthenticationManager
的 bean 来提供它,Spring 尝试实例化它,而
AuthenticationConfiguration
尝试获取它。 . 你明白了


以下是定义authenticationManager的方法:

@Bean
public AuthenticationManager authenticationManager(HttpSecurity http, PasswordEncoder passwordEncode, UserDetailsService userDetailsService) throws Exception {
    AuthenticationManagerBuilder authenticationManagerBuilder =
                http.getSharedObject(AuthenticationManagerBuilder.class);

    return authenticationManagerBuilder
                .userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder)
                .build();
}
© www.soinside.com 2019 - 2024. All rights reserved.