SpringBoot -> 3.1.4
Reproduction steps:
1: Define AuthenticationManager, using only the default
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
2: Define providers, such as UsernamePasswordProvider, PhoneSmsProvider
@Bean
public UsernamePasswordProvider usernamePasswordProvider(PasswordEncoder passwordEncoder, @Qualifier("usernamePasswordAuthUserDetailService") AuthUserDetailService userDetailsService) {
return new UsernamePasswordProvider(passwordEncoder, userDetailsService);
}
@Bean
public PhoneSmsProvider phoneSmsProvider(@Qualifier("phoneSmsAuthUserDetailService") AuthUserDetailService authUserDetailService) {
return new PhoneSmsProvider(authUserDetailService);
}
public class UsernamePasswordProvider implements AuthenticationProvider {
private final PasswordEncoder passwordEncoder;
private final AuthUserDetailService userDetailsService;
public UsernamePasswordProvider(PasswordEncoder passwordEncoder, AuthUserDetailService userDetailsService) {
this.passwordEncoder = passwordEncoder;
this.userDetailsService = userDetailsService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// doSomething
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.isAssignableFrom(UsernamePasswordToken.class);
}
}
public class PhoneSmsProvider implements AuthenticationProvider {
private final AuthUserDetailService authUserDetailService;
public PhoneSmsProvider(AuthUserDetailService authUserDetailService) {
this.authUserDetailService = authUserDetailService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// doSomething
return null;
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.isAssignableFrom(PhoneSmsToken.class);
}
}
3: Turn off default form authentication, customize form authentication, and add it to HttpSecurity
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http,
AuthenticationManager authenticationManager)
throws Exception {
// 禁用表单登录
http.formLogin(AbstractHttpConfigurer::disable);
// 禁用csrf
http.csrf(AbstractHttpConfigurer::disable);
// 登录表单认证器
http.addFilterAt(baseAuthenticationFilter(authenticationManager), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
public class BaseAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private final StrategyMachine<LoginType, AuthenticationContext,Authentication> machine;
private static final String LOGIN_TYPE_PARAMETER = "loginType";
public BaseAuthenticationFilter(String defaultFilterProcessesUrl, StrategyMachine<LoginType, AuthenticationContext, Authentication> machine) {
super(new AntPathRequestMatcher(defaultFilterProcessesUrl, HttpMethod.POST.name()));
this.machine = machine;
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
String loginType = obtainLoginType(request);
if (!StringUtils.hasText(loginType)) {
UsernamePasswordToken .....
return this.getAuthenticationManager().authenticate(usernamePasswordToken);
}
if ("phoneSms".equls(loginType)) {
PhoneSmsToken ....
return this.getAuthenticationManager().authenticate(phoneSmsToken);
}
return null;
}
private String obtainLoginType(HttpServletRequest request) {
return request.getParameter(LOGIN_TYPE_PARAMETER);
}
}
4: When we run, we will find that AuthenticationManager has entered a dead loop StackOverflowError generated
5: My solution is to modify the generation method of AuthenticationManager, block the default AuthenticationManager, customize ProviderManager, run it again, and the problem is resolved
@Bean
public AuthenticationManager authenticationManager(UsernamePasswordProvider usernamePasswordProvider,PhoneSmsProvider phoneSmsProvider) throws Exception {
return new ProviderManager(List.of(usernamePasswordProvider,phoneSmsProvider));
}
6: Therefore, I think it should be a bug in multiple providers Because I think it's common for it to support multiple providers, and although I have solved this problem, every time I add a new provider, I need to inject it into the AuthenticationManager instead of automatically, which is a very annoying thing. Therefore, I want to solve this problem or find a way to get all the providers so that I don't have to modify the ProviderManager every time
Comment From: zhangpan-soft
I found a way to find all the providers
@Bean
public AuthenticationManager authenticationManager() throws Exception {
Map<String, AuthenticationProvider> beansOfType = SpringContextUtil.getApplicationContext().getBeansOfType(AuthenticationProvider.class);
List<AuthenticationProvider> providers = new ArrayList<>();
beansOfType.forEach(((s, authenticationProvider) -> {
providers.add(authenticationProvider);
}));
return new ProviderManager(providers);
}
Comment From: marcusdacoregio
Hi, @zhangpan-soft. I think that problem is a duplicate of https://github.com/spring-projects/spring-security/issues/12343. Can you read the related issues, apply the suggested fixes and read this section of the docs https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/index.html#customize-global-authentication-manager
That should give you a good idea of how to solve that.
I'm closing this since it is not a bug in Spring Security; however, after going through all those issues you still think this is a bug, we can reopen this ticket.
Comment From: zhangpan-soft
Hi, @zhangpan-soft. I think that problem is a duplicate of #12343. Can you read the related issues, apply the suggested fixes and read this section of the docs https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/index.html#customize-global-authentication-manager
That should give you a good idea of how to solve that.
I'm closing this since it is not a bug in Spring Security; however, after going through all those issues you still think this is a bug, we can reopen this ticket.
It is this principle, in the second clause of https://github.com/spring-projects/spring-security/issues/12343, but I have seen that it was proposed in 2021 and seems to have been resolved. However, I still encounter this problem using the latest Springboot security I don't think this problem has been fundamentally solved You can only use Spring Boot Security without using oauth2, as this issue is not related to oauth2 In addition, and https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/index.html#customize -The global authentication manager has some differences. I have blocked the entire authentication filter chain and only retained the SecurityContextHolder and ProviderManager (in fact, this is not the point, the point is that when there are multiple ProviderManagers, StackOverflow will occur at project startup. Moreover, it is a hidden error that needs to be seen in the debugger. If authentication is requested, it will become explicit) The issue has not been resolved with the latest Spring Boot Security
Comment From: zhangpan-soft
Hi, @zhangpan-soft. I think that problem is a duplicate of #12343. Can you read the related issues, apply the suggested fixes and read this section of the docs https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/index.html#customize-global-authentication-manager
That should give you a good idea of how to solve that.
I'm closing this since it is not a bug in Spring Security; however, after going through all those issues you still think this is a bug, we can reopen this ticket.
This is my configuration core code. I have removed other implementations such as controllers and need to make a simple modification I have used a custom package and need to download it through settings.xml
Comment From: zhangpan-soft
Hi, @zhangpan-soft. I think that problem is a duplicate of #12343. Can you read the related issues, apply the suggested fixes and read this section of the docs https://docs.spring.io/spring-security/reference/servlet/authentication/passwords/index.html#customize-global-authentication-manager That should give you a good idea of how to solve that. I'm closing this since it is not a bug in Spring Security; however, after going through all those issues you still think this is a bug, we can reopen this ticket.
This is my configuration core code. I have removed other implementations such as controllers and need to make a simple modification I have used a custom package and need to download it through settings.xml
Need to change IP address to https://nexus.51000.net
Comment From: marcusdacoregio
The problem has been resolved by guiding users on not using the AuthenticationManagerBuilder#getAuthenticationManager to create the AuthenticationManager bean, you should create your own and expose it. This answer provides more details.
If you think there is a bug, please provide a minimal, reproducible sample, where the dependencies are all resolved from Maven Central.
Comment From: zhangpan-soft
The problem has been resolved by guiding users on not using the
AuthenticationManagerBuilder#getAuthenticationManagerto create theAuthenticationManagerbean, you should create your own and expose it. This answer provides more details.If you think there is a bug, please provide a minimal, reproducible sample, where the dependencies are all resolved from Maven Central.
thank you. I understand