Home > Software design >  Is there a way to configure password encoder for default password?
Is there a way to configure password encoder for default password?

Time:12-02

I use overridden values for spring security username and password. Following properties are in my application.properties.

spring.security.user.name=myUser
spring.security.user.password=myPassword
spring.security.user.roles=admin

I would like to encrypt the password value as follows:

spring.security.user.name=myUser
spring.security.user.password={bcrypt}hashedpassword valuevalue
spring.security.user.roles=admin

I added PasswordEncoder in my SpringConfig:

@Bean
public PasswordEncoder encoder() {
    return new BCryptPasswordEncoder();
}

In some example I noticed that there is for AuthenitcationManagerBuilder but I do not know what datasource should be used. What else do I need to use encrypted password for default user?

@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {

Adding my Spring Security config as a reference:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/api/v1/custom").hasRole("admin")
                .anyRequest().authenticated()
                .and()
            .httpBasic();

        http
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Bean
    public PasswordEncoder encoder() {
        return new BCryptPasswordEncoder();
    }

CodePudding user response:

Grabbing:

$2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS

for input:

hello

... from (e.g. thx): https://bcrypt-generator.com/

With:

  • spring-starter (web, security),
  • application.properties:
    spring.security.user.password={bcrypt}$2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS
    
  • and any (rest)controller:
    package com.example.demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @SpringBootApplication
    @RestController
    public class DemoApplication {
    
       public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
       }
    
       //  @Bean
       //  public PasswordEncoder encoder() {
       //   return new BCryptPasswordEncoder();
       //  }
    
       @GetMapping("secured")
       public String secured() {
        return "Hello Admin";
       }
    }
    
  • and without additional config,
  • we can login with user=user and password=hello.

Adding:

@Bean
public PasswordEncoder encoder() {
  return new BCryptPasswordEncoder();
}

breaks this behavior!! To "fix it", we can (application.properties):

spring.security.user.password=$2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS

.. omit the "tag" {bcrypt}! ...

Responsible for "tags"/capable of several algorithms is the DelegatingPasswordEncoder, which is auto configured (via [AuthenticationConfiguration|HttpSecurityConfiguration].LazyPasswordEncoder ..if no other PasswordEncoder bean found) and uses PasswordEncoderFactories behind the scenes, like (currently) this:

@SuppressWarnings("deprecation")
public static PasswordEncoder createDelegatingPasswordEncoder() {
  String encodingId = "bcrypt";
  Map<String, PasswordEncoder> encoders = new HashMap<>();
  encoders.put(encodingId, new BCryptPasswordEncoder());
  encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
  encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
  encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
  encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
  encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5());
  encoders.put("pbkdf2@SpringSecurity_v5_8", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8());
  encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1());
  encoders.put("scrypt@SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());
  encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
  encoders.put("SHA-256",
          new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
  encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
  encoders.put("argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());
  encoders.put("argon2@SpringSecurity_v5_8", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());
  return new DelegatingPasswordEncoder(encodingId, encoders);
}

EDIT:

No, also introducing:

@Configuration
class WebSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
      http
          .csrf()
          .disable()
          .authorizeHttpRequests().requestMatchers("/secured").hasRole("admin")
          .anyRequest()
          .authenticated()
          .and()
          .httpBasic();

      http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
      return http.build();
    }
  }
}

Plus:

spring.security.user.roles=admin

Doesn't change my observations:

  • When "custom password encoder"
  • and $2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS
    • can login with user:hello (and view /secured response)
  • with default/-out password encoder
  • and {bcrypt}$2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS
    • can login with user:hello (and view /secured response)

(it only switches from form to basic login:)

Test (good/app config):

package com.example.demo;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest
class WithAppConfigTest {

  @Autowired
  MockMvc mockMvc;

  @Test
  void expectAccess() throws Exception {
    mockMvc.perform(
        get("/secured")
            .with(httpBasic("user", "hello"))
    ).andExpectAll(
        authenticated(),
        status().isOk(),
        content().string("Hello Admin"));
  }

  @Test
  void expectUnauthorized() throws Exception {
    mockMvc.perform(
        get("/secured")
            .with(httpBasic("user", "wrong"))
    ).andExpectAll(
        unauthenticated(),
        status().isUnauthorized(),
        content().bytes(new byte[0]));
  }
}

Test forbidden:

@WebMvcTest(properties = "spring.security.user.roles=foo,bar,baz")
class WithFakeConfigTest {

  @Autowired
  MockMvc mockMvc;

  @Test
  void expectForbidden() throws Exception {
    mockMvc.perform(
        get("/secured")
            .with(httpBasic("user", "hello"))
    ).andExpectAll(
        authenticated(), // !, but:
        status().isForbidden(),
        content().bytes(new byte[0]));
  }
}
  • Related