PasswordEncoder in Spring Security

PasswordEncoder

Ohne weiteres Zutun legt Spring Security die Passwörter unverschlüssel ab. Das sollte heute aber nicht mehr der Fall sein, da zum Beispiel wenn die Passwörter der Benutzer in einer Datenbank gespeichert werden diese bei einem Einbruch als Plaintext vorliegen. Daher werden (sollten / es gibt immer wieder Beispiele die das Gegenteil beweisen) die Passwörter verschlüsselt abgelegt. Dieses kann zum Beispiel ein Hash des Passwortes sein.

Um Spring Security flexible zu gestalten, kann man zur Laufzeit einen PasswordEncoder setzen. Die Einstellungen die die Sicherheit einer Spring Anwendung betreffen, werden in einer WebSecurityConfigurerAdapter erweiterten Konfiguration vorgenommen. Hierfür muss die Einstellung für die Authentifikation geändert werden. Dafür die ist die Methode passwordEncoder() verantwortlich.

Als ein guter Kandidat für die verschlüsselte Ablage ist BCrypt. In Spring Security implementiert die Klasse BCryptPasswordEncoder den BCrypt Algorithmus.

Damit dieses per DI injeziert werden kann, benötigen wir eine Bean die uns den BCryptPasswordEncoder liefert.

@Bean PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

Jetzt kann ein PasswordEncoder per @Autowired zur Laufzeit gesetzt werden und als Parameter für passwordEncoder() Funktion dienen.

@Autowired
private PasswordEncoder passwordEncoder;

Grundgerüst einer Anwendung

Dependency

Als Abhängigkeit ist natürlich spring-boot-starter-security einzufügen.

dependencies {
    // Spring Boot
    ...
    implementation('org.springframework.boot:spring-boot-starter-security')
    ... 

Konfiguration WebSecurityConfigurerAdapter

Hier ein sehr simples Beispiel mit einer in Memory Benutzerauthentifizierung. Ich glaube jeder hat schon einmal die verwendeten Benutzernamen und Passwörter gesehen… 🙂

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Bean PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .passwordEncoder(passwordEncoder)
            .withUser("user")
            .password("$2a$10$9z954e6ETmwauiiBbwCNbOwU90UnUrh9hpRUWwNmWUx6aFic2nE46") // user
            .roles("USER")
                .and()
            .withUser("admin")
            .password("$2a$10$Tljz0HJByMV97Y92B65QBu0TckqbJDlT1kgmibMijhbMtpHPORanK") // admin
            .roles("ADMIN");
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/admin/**").hasAnyRole("ADMIN")
                .anyRequest().hasAnyRole("USER").and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/allcustomers")
                .permitAll();
    }
}