Monday, June 18, 2018

Basic Auth does not work with multiple configuration

Leave a Comment

I'm trying to setup basic authentication for one specific path in an application with already some configured security policies. I'm using Spring Boot 2.0

Here's my configuration:

@Configuration @EnableWebSecurity class SecurityConfig {      @Configuration     @Order(1)     inner class TokenWebSecurityConfig : WebSecurityConfigurerAdapter() {          override fun configure(http: HttpSecurity) {             http.antMatcher("/token")                 .authorizeRequests()                 .antMatchers(HttpMethod.POST, "/token").permitAll()                 .anyRequest().denyAll()                 .and()                 .sessionManagement()                 .sessionCreationPolicy(SessionCreationPolicy.STATELESS)                 .and()                 .csrf().disable()         }     }      @Configuration     @Order(2)     inner class SignUpWebSecurityConfig(private val signUpBasicAuthConfig: SignUpBasicAuthConfig) :         WebSecurityConfigurerAdapter() {          override fun configure(http: HttpSecurity) {             http                 .antMatcher("/signup")                 .csrf()                 .disable()                 .authorizeRequests()                 .anyRequest().authenticated()                 .and()                 .httpBasic()                 .and()                 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)         }          override fun configure(auth: AuthenticationManagerBuilder) {             auth.inMemoryAuthentication()                 .withUser(signUpBasicAuthConfig.username)                 .password(signUpBasicAuthConfig.password)                 .roles("USER")         }     }      @Configuration     @Order(3)     inner class ApiWebSecurityConfig(private val service: TokenAuthenticationUserDetailsService) :         WebSecurityConfigurerAdapter() {          override fun configure(http: HttpSecurity) {             http                 .antMatcher("/api/**")                 .authorizeRequests()                 .anyRequest().authenticated()                 .and()                 .addFilterBefore(authFilter(), RequestHeaderAuthenticationFilter::class.java)                 .authenticationProvider(preAuthProvider())                 .sessionManagement()                 .sessionCreationPolicy(SessionCreationPolicy.STATELESS)                 .and()                 .csrf().disable()         }          @Bean         fun authFilter(): TokenAuthenticationFilter = TokenAuthenticationFilter()          @Bean         fun preAuthProvider(): AuthenticationProvider =             PreAuthenticatedAuthenticationProvider().apply { setPreAuthenticatedUserDetailsService(service) }     }      @Configuration     @Order(4)     inner class HealthWebSecurityConfig : WebSecurityConfigurerAdapter() {          override fun configure(http: HttpSecurity) {             http.authorizeRequests().antMatchers(HttpMethod.GET, "/health").permitAll()         }     }      @Configuration     class AuthenticationManagerProvider : WebSecurityConfigurerAdapter() {          @Bean         override fun authenticationManagerBean(): AuthenticationManager = super.authenticationManagerBean()     } } 

But the @Order(2) configuration always fails. Logs:

14:23:00.318 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/signup'; against '/token' 14:23:00.319 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/signup'; against '/signup' 14:23:00.320 [http-nio-8080-exec-1] DEBUG o.s.security.web.FilterChainProxy - /signup at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter' 14:23:00.321 [http-nio-8080-exec-1] DEBUG o.s.security.web.FilterChainProxy - /signup at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter' 14:23:00.322 [http-nio-8080-exec-1] DEBUG o.s.security.web.FilterChainProxy - /signup at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter' 14:23:00.324 [http-nio-8080-exec-1] DEBUG o.s.security.web.FilterChainProxy - /signup at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter' 14:23:00.324 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', GET] 14:23:00.324 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request 'POST /signup' doesn't match 'GET /logout 14:23:00.324 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', POST] 14:23:00.324 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Checking match of request : '/signup'; against '/logout' 14:23:00.324 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', PUT] 14:23:00.325 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request 'POST /signup' doesn't match 'PUT /logout 14:23:00.325 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - Trying to match using Ant [pattern='/logout', DELETE] 14:23:00.325 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.m.AntPathRequestMatcher - Request 'POST /signup' doesn't match 'DELETE /logout 14:23:00.325 [http-nio-8080-exec-1] DEBUG o.s.s.w.u.matcher.OrRequestMatcher - No matches found 14:23:00.325 [http-nio-8080-exec-1] DEBUG o.s.security.web.FilterChainProxy - /signup at position 5 of 11 in additional filter chain; firing Filter: 'BasicAuthenticationFilter' 14:23:00.325 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.w.BasicAuthenticationFilter - Basic Authentication Authorization header found for user 'username' 14:23:00.327 [http-nio-8080-exec-1] DEBUG o.s.s.authentication.ProviderManager - Authentication attempt using org.springframework.security.authentication.dao.DaoAuthenticationProvider 14:23:00.473 [http-nio-8080-exec-1] WARN  o.s.s.c.bcrypt.BCryptPasswordEncoder - Encoded password does not look like BCrypt 14:23:00.473 [http-nio-8080-exec-1] DEBUG o.s.s.a.d.DaoAuthenticationProvider - Authentication failed: password does not match stored value 14:23:00.474 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.w.BasicAuthenticationFilter - Authentication request for failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials 14:23:00.474 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.DelegatingAuthenticationEntryPoint - Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest] 14:23:00.474 [http-nio-8080-exec-1] DEBUG o.s.s.w.a.DelegatingAuthenticationEntryPoint - No match found. Using default entry point org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint@44d29834 

It seems to me that for some reason the in memory AuthenticationManager is not used. Any idea?

2 Answers

Answers 1

I had a similair issue, since spring 2.0 the passwords need to have a prefix indicating how they are encrypted. For example a bcrypt password would look like this:

{bcrypt}2187jbfsafsd 

If this does not work immediately I also made a few more edits to make it work in my application:

I first created a Passwordencoder bean and pasted it in the securityconfig:

@Bean public static PasswordEncoder passwordEncoder() {    return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } 

Then I modified the DaoAuthenticationProvider (also in the securityconfig):

@Bean public DaoAuthenticationProvider authenticationProvider() {     final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();     authProvider.setUserDetailsService(userDetailsService);     authProvider.setPasswordEncoder(passwordEncoder);     return authProvider; } 

And lastly I autowired the encoder into the securityconfig:

@Autowired private PasswordEncoder passwordEncoder; 

I hope this helps you

Answers 2

I think your problem relates to having multiple WebSecurityConfigurerAdapter instances and expect them all to be used, as far as I know Spring will only use one. This has been discussed further here: Using multiple WebSecurityConfigurerAdapter in spring boot.

My preferred solution is to define a special interface

public interface ServiceWebSecurityConfigurer {     void configure(HttpSecurity http) throws Exception; } 

Then have just one ConfigurerAdapter:

public class MyConfigurerAdapter extends WebSecurityConfigurerAdapter {      @Autowired(required = false)     List<ServiceWebSecurityConfigurer> securityConfigurers;      public void configure(HttpSecurity http) throws Exception {         http.authorizeRequests(). // whatever          for (ServiceWebSecurityConfigurer serviceSecConfig: securityConfigurers)             serviceSecConfig.configure(http);          http.authorizeRequests(). // whatever     } } 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment