Home > database >  BcryptEncoder configuration in Spring Security Configuration
BcryptEncoder configuration in Spring Security Configuration

Time:07-07

So I'm creating this restservice but I am struggling with the encoderconfiguration.

I created a config class to set the passwordencoderBean, as instructed in replies below.

My code compiles. But when I try to log in, I get 'Bad Credentials', and yes I am sure I am using right credentials. Also Yes, the password in my database is Bcryptencoded with {bcrypt} in front of it. My guess is that I am misconfiguring this passwordEncoder configuration.Where lies the fault in configuration?

Here below my passwordencodeConfig:

@Configuration
public class PasswordEncoderConfig {
    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
}

current SpringSecurityConfiguration:


@EnableWebSecurity
class SecurityConfiguration extends WebSecurityConfigurerAdapter{
    private static final String ADMIN = "ROLE_ADMIN";
    private static final String WORKER = "ROLE_WORKER";
    
    private final DataSource dataSource;
    private PasswordEncoder bcryptencoder;
    
    public SecurityConfiguration(DataSource dataSource,  PasswordEncoder bcryptencoder) {
        this.dataSource = dataSource;
        this.bcryptencoder = bcryptencoder;
    }
    
    /*@Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }*/
    
    
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource)
            .usersByUsernameQuery("select voornaam as username, password as password, true as enabled from gebruikers where voornaam = ?")
            .passwordEncoder(bcryptencoder)
            .authoritiesByUsernameQuery("select voornaam as username, role as authorities from gebruikers where voornaam = ?");
        
            
    }
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
        .mvcMatchers("/images/**")
        .mvcMatchers("/css/**")
        .mvcMatchers("/js/**");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable();
        /*remove after postman, @cross origin*/
        http.formLogin();
        http.authorizeHttpRequests(requests -> requests
            .mvcMatchers("/**").hasAnyAuthority(ADMIN, WORKER)
            .mvcMatchers("/gebruikers/**").hasAnyAuthority(ADMIN, WORKER));
        http.logout();
        
    }
    


}

CodePudding user response:

The way to go is to use

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

It uses BCrypt as the default but provides a better interface for future migration. Note that the generated password has a prefix:

With Factory:
{bcrypt}$2a$10$Zz3xIJON0d1GI0vqMULIKOHCImVnFCWMNRE3Vw0ElvSmfCqGcDV5W

Without:
$2a$10$Zz3xIJON0d1GI0vqMULIKOHCImVnFCWMNRE3Vw0ElvSmfCqGcDV5W

When you use the factory and provided a bcrypt hash without the prefix, it will be rejected as invalid.

Edit: And as Chaosfire said it, you defined a circular bean definition. Instead of injecting it to a field, you can use the method for the bean declaration, spring will inject the instance to the method call, so you end up with the same password encoder, which you provided at the bean declaration.

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.jdbcAuthentication().dataSource(dataSource)
        .usersByUsernameQuery("select voornaam as username, password as password, true as enabled from gebruikers where voornaam = ?")
        .passwordEncoder(passwordEncoder()) // referencing bean, not field
        .authoritiesByUsernameQuery("select voornaam as username, role as authorities from gebruikers where voornaam = ?");

CodePudding user response:

It would be better to define the password encoder in a different config class(but it's not a necessity). Something like this:

@Configuration
public class BeanConfig {

  @Bean
  public PasswordEncoder passwordEncoder() {
    return desired encoder;
  }
}

Side note - you are creating a circular dependency by defining it in SecurityConfiguration.

Change the field to PasswordEncoder, and make constructor accept PasswordEncoder as well.

@EnableWebSecurity
class SecurityConfiguration extends WebSecurityConfigurerAdapter {
  
  //other fields

  private final DataSource dataSource;
  private final PasswordEncoder bcryptencoder;

  public SecurityConfiguration(DataSource dataSource, PasswordEncoder bcryptencoder) {
    this.dataSource = dataSource;
    this.bcryptencoder = bcryptencoder;
  }

  //other configs
}

The bean is declared as PasswordEncoder and spring is not aware of its' concrete type(BCryptPasswordEncoder), that's why it can't find a bean of type BCryptPasswordEncoder. Also, using the concrete implementation(BCryptPasswordEncoder) creates a tight coupling, use the interface so your components are loosely coupled - the abstraction is already created for you, there is no reason not to use it.

  • Related