我在 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 注释,我仍然遇到循环依赖错误。关于如何解决这个问题有什么建议或建议吗?
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();
}