3

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?

andrew
  • 3,819
  • 4
  • 21
  • 43
  • It seems to be something related to the PasswordEncodoer. I saw this WARN in your logs: `14:23:00.473 [http-nio-8080-exec-1] WARN o.s.s.c.bcrypt.BCryptPasswordEncoder - Encoded password does not look like BCrypt` – Angelo Immediata Jun 11 '18 at 09:57
  • @AngeloImmediata but no password encoder should be used in this case. That's the thing, I don't think the correct authentication manager is been used – andrew Jun 11 '18 at 14:06
  • 2
    Most likely related: https://stackoverflow.com/a/49312787/3620458 – Bragolgirith Jun 11 '18 at 16:58
  • I think you may be missing the `http.addFilterAfter(new CustomFilter(), BasicAuthenticationFilter.class);` in the `SignUpWebSecurityConfig`. See http://www.baeldung.com/spring-security-basic-authentication – Tarun Lalwani Jun 12 '18 at 02:52
  • I had a similar warn about BCrypt in inMemoryAuthentication when I migrate my application to Spring Boot 2.0. A password that would be "123456" should be "{noop}123456"..... So, to test if it workd try to change your inMemoryAuthentication configuration to: auth.inMemoryAuthentication() .withUser(signUpBasicAuthConfig.username) .password("{noop}" + signUpBasicAuthConfig.password) .roles("USER") – Norberto Ritzmann Jun 13 '18 at 19:40

2 Answers2

0

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

Sven Hakvoort
  • 3,253
  • 2
  • 16
  • 33
0

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
    }
}
eis
  • 49,147
  • 13
  • 140
  • 191