Home > Software engineering >  How to bind multiple AuthenticationManager on path?
How to bind multiple AuthenticationManager on path?

Time:07-25

How can I tell spring to bind bind a AuthenticationManager to certain paths?

The following should match a basic authentication manager to all /rest/** paths, and ldap authentication to all other paths.

public SecurityFilterChain securityFilterChain(HttpSecurity http,
                                                @Qualifier("basicAuthenticationManager") AuthenticationManager basicAuthenticationManager,
                                                @Qualifier("ldapAuthenticationManager") AuthenticationManager ldapAuthenticationManager) {
                                         
    return http.authorizeHttpRequests(auth -> auth.mvcMatchers("/rest/**").authenticated())
            .httpBasic(withDefaults())
            .authenticationManager(basicAuthenticationManager)
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .formLogin(withDefaults())
            .authenticationManager(ldapAuthenticationManager)
            .build();
}

Result: when I'm accessing localhost:8080/rest/test with basic authentication, I'm getting a 401. Thus I assume the configuration is somehow wrong.

Because when I configure the application with only one AuthenticationManager, everything works as expected:

public SecurityFilterChain securityFilterChain(HttpSecurity http,
                                                @Qualifier("basicAuthenticationManager") AuthenticationManager basicAuthenticationManager,
                                                @Qualifier("ldapAuthenticationManager") AuthenticationManager ldapAuthenticationManager) {
                                         
    return http.authorizeHttpRequests(auth -> auth.mvcMatchers("/rest/**").authenticated())
            .httpBasic(withDefaults())
            .authenticationManager(basicAuthenticationManager)
            .build();
}

What might be wrong here? Is it the order in the chain?

CodePudding user response:

You need to create multiple SecurityFilterChain beans, one bean can only have 1 authentication manager. In your case the cleanest solution in my opinion would be this.


@Order(1)
public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http,
                                                @Qualifier("basicAuthenticationManager") AuthenticationManager basicAuthenticationManager) {
                                         
    return http.antMatcher("/rest/**")
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .httpBasic(withDefaults())  
            .authenticationManager(basicAuthenticationManager)
            .build();
}

public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http,
                                                @Qualifier("ldapAuthenticationManager") AuthenticationManager ldapAuthenticationManager) {
                                         
    return http
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .formLogin(withDefaults())          
            .authenticationManager(ldapAuthenticationManager)
            .build();
}

Alternativly you can use AuthenticationProvider instead of AuthenticationManager you can register more than one of those for each SecurityFilterChain. you would do that something like this (mock code)

@Bean
public SecurityFilterChain filterChain(HttpSecurity http, LdapAuthenticator ldapAuthenticator) throws Exception
{
    http
            .authorizeHttpRequests(auth -> auth
                .antMatchers("/rest/**").hasRole("SERVICE") //Something to identify user as another sytem calling your API
                .anyRequest().authenticated())
            .formLogin(withDefaults())
            .httpBasic(withDefaults())            
            .authenticationManager(ldapAuthenticationManager);

    LdapAuthoritiesPopulator ldapAuthoritiesPopulator = new UserDetailsServiceLdapAuthoritiesPopulator(userDetails);

    http.authenticationProvider(new LdapAuthenticationProvider(ldapAuthenticator, ldapAuthoritiesPopulator));

    return http.build();
}

@Bean
BindAuthenticator ldapAuthenticator(BaseLdapPathContextSource ldapContextSource)
{
    BindAuthenticator authenticator = new BindAuthenticator(ldapContextSource);
    authenticator.setUserSearch(new FilterBasedLdapUserSearch("ou=people", "(uid={0})", ldapContextSource));
        
    return authenticator;
}
  • Related