Issue
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?
Solution
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;
}
Answered By - Ralan
Answer Checked By - Marilyn (JavaFixing Volunteer)