Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In the case of multiple providers, the AuthenticationManager will display StackOverflow #14226

Closed
zhangpan-soft opened this issue Dec 1, 2023 · 7 comments
Assignees
Labels
status: invalid An issue that we don't feel is valid type: bug A general bug

Comments

@zhangpan-soft
Copy link

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
image

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));
    }

image

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

@zhangpan-soft zhangpan-soft added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Dec 1, 2023
@zhangpan-soft
Copy link
Author

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);
    }

@marcusdacoregio marcusdacoregio self-assigned this Dec 1, 2023
@marcusdacoregio marcusdacoregio removed the status: waiting-for-triage An issue we've not yet triaged label Dec 1, 2023
@marcusdacoregio
Copy link
Contributor

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.

@marcusdacoregio marcusdacoregio added the status: invalid An issue that we don't feel is valid label Dec 1, 2023
@zhangpan-soft
Copy link
Author

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 #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

@zhangpan-soft
Copy link
Author

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.

blind-box.zip

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

@zhangpan-soft
Copy link
Author

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.

blind-box.zip

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

@marcusdacoregio
Copy link
Contributor

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.

@zhangpan-soft
Copy link
Author

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.

thank you. I understand

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: invalid An issue that we don't feel is valid type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants