diff --git a/spring-5-security/src/main/java/com/baeldung/passwordstorage/BaeldungPasswordEncoderSetup.java b/spring-5-security/src/main/java/com/baeldung/passwordstorage/BaeldungPasswordEncoderSetup.java index 89eeb0f826..94edf85631 100644 --- a/spring-5-security/src/main/java/com/baeldung/passwordstorage/BaeldungPasswordEncoderSetup.java +++ b/spring-5-security/src/main/java/com/baeldung/passwordstorage/BaeldungPasswordEncoderSetup.java @@ -1,33 +1,41 @@ package com.baeldung.passwordstorage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.DelegatingPasswordEncoder; +import org.springframework.security.authentication.AuthenticationEventPublisher; +import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.event.AuthenticationSuccessEvent; +import org.springframework.security.core.Authentication; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder; - -import java.util.HashMap; -import java.util.Map; @Configuration public class BaeldungPasswordEncoderSetup { + private final static Logger LOG = LoggerFactory.getLogger(BaeldungPasswordEncoderSetup.class); + @Bean - public PasswordEncoder passwordEncoder() { - // set up the list of supported encoders and their prefixes - String encodingId = "rot13"; - Map encoders = new HashMap<>(); - encoders.put(encodingId, new Rot13PasswordEncoder()); - encoders.put("scrypt", new SCryptPasswordEncoder()); - encoders.put("bcrypt", new BCryptPasswordEncoder()); + public AuthenticationEventPublisher authenticationEventPublisher(final ApplicationEventPublisher publisher) { + return new DefaultAuthenticationEventPublisher(publisher); + } - // get an instance of the DelegatingPasswordEncoder, set up to use our instance as default encoder - DelegatingPasswordEncoder delegatingPasswordEncoder = new DelegatingPasswordEncoder(encodingId, encoders); + @Bean + public ApplicationListener authenticationSuccessListener(final PasswordEncoder encoder) { + return (AuthenticationSuccessEvent event) -> { + final Authentication authentication = event.getAuthentication(); - // configure our instance as default encoder for actual matching - delegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(encoders.get(encodingId)); + if (authentication instanceof UsernamePasswordAuthenticationToken && authentication.getCredentials() != null) { + final CharSequence clearTextPassword = (CharSequence) authentication.getCredentials(); // 1 + final String newPasswordHash = encoder.encode(clearTextPassword); // 2 - return delegatingPasswordEncoder; + LOG.info("New password hash {} for user {}", newPasswordHash, authentication.getName()); + + ((UsernamePasswordAuthenticationToken) authentication).eraseCredentials(); // 3 + } + }; } } diff --git a/spring-5-security/src/main/java/com/baeldung/passwordstorage/PasswordStorageApplication.java b/spring-5-security/src/main/java/com/baeldung/passwordstorage/PasswordStorageApplication.java new file mode 100644 index 0000000000..173d979a45 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/passwordstorage/PasswordStorageApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.passwordstorage; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class PasswordStorageApplication { + + public static void main(String[] args) { + SpringApplication.run(PasswordStorageApplication.class, args); + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/passwordstorage/PasswordStorageWebSecurityConfigurer.java b/spring-5-security/src/main/java/com/baeldung/passwordstorage/PasswordStorageWebSecurityConfigurer.java new file mode 100644 index 0000000000..c73461daaa --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/passwordstorage/PasswordStorageWebSecurityConfigurer.java @@ -0,0 +1,57 @@ +package com.baeldung.passwordstorage; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationEventPublisher; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.DelegatingPasswordEncoder; +import org.springframework.security.crypto.password.MessageDigestPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class PasswordStorageWebSecurityConfigurer extends WebSecurityConfigurerAdapter { + + private final AuthenticationEventPublisher eventPublisher; + private final UserDetailsService userDetailsService; + + @Autowired + public PasswordStorageWebSecurityConfigurer(AuthenticationEventPublisher eventPublisher, UserDetailsService userDetailsService) { + this.eventPublisher = eventPublisher; + this.userDetailsService = userDetailsService; + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.eraseCredentials(false) // 4 + .authenticationEventPublisher(eventPublisher) + .userDetailsService(userDetailsService) + .passwordEncoder(passwordEncoder()); + } + + @Bean + public PasswordEncoder passwordEncoder() { + // set up the list of supported encoders and their prefixes + String encodingId = "bcrypt"; + Map encoders = new HashMap<>(); + encoders.put(encodingId, new BCryptPasswordEncoder()); + encoders.put("scrypt", new SCryptPasswordEncoder()); + encoders.put("SHA-256", new MessageDigestPasswordEncoder("SHA-256")); + + // get an instance of the DelegatingPasswordEncoder, set up to use our instance as default encoder + DelegatingPasswordEncoder delegatingPasswordEncoder = new DelegatingPasswordEncoder(encodingId, encoders); + + // configure our instance as default encoder for actual matching + delegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(encoders.get(encodingId)); + + return delegatingPasswordEncoder; + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/passwordstorage/Rot13PasswordEncoder.java b/spring-5-security/src/main/java/com/baeldung/passwordstorage/Rot13PasswordEncoder.java deleted file mode 100644 index 52de412de7..0000000000 --- a/spring-5-security/src/main/java/com/baeldung/passwordstorage/Rot13PasswordEncoder.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.baeldung.passwordstorage; - -import org.springframework.security.crypto.password.PasswordEncoder; - -/** - * DISCLAIMER: Never ever use this in any production environment! - *

- * Does only work for characters. - */ -public class Rot13PasswordEncoder implements PasswordEncoder { - - @Override - public String encode(CharSequence rawPassword) { - StringBuffer result = new StringBuffer(rawPassword.length()); - rawPassword - .chars() - .forEach(charCode -> { - if (charCode >= 65 && charCode <= 77 || charCode >= 97 && charCode <= 109) { - result.append(Character.toChars(charCode + 13)); - } else if (charCode >= 78 && charCode <= 90 || charCode >= 110 && charCode <= 133) { - result.append(Character.toChars(charCode - 13)); - } - }); - - return result.toString(); - } - - @Override - public boolean matches(CharSequence rawPassword, String encodedPassword) { - return encode(rawPassword).equals(encodedPassword); - } -} diff --git a/spring-5-security/src/test/java/com/baeldung/passwordstorage/Rot13PasswordEncoderTest.java b/spring-5-security/src/test/java/com/baeldung/passwordstorage/Rot13PasswordEncoderTest.java deleted file mode 100644 index b515d47fbc..0000000000 --- a/spring-5-security/src/test/java/com/baeldung/passwordstorage/Rot13PasswordEncoderTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.baeldung.passwordstorage; - -import org.junit.Test; - -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; - -public class Rot13PasswordEncoderTest { - - private final Rot13PasswordEncoder encoder = new Rot13PasswordEncoder(); - - @Test - public void givenEncodedPassword_whenEncodeIsCalledWithTheEncodedPassword_thenTheClearTextPassword() { - String password = "baeldung"; - String encoded = encoder.encode(password); - String actualResult = encoder.encode(encoded); - - assertThat(actualResult, is(password)); - } - - @Test - public void givenCorrectPassword_whenMatchesIsCalled_thenReturnTrue() { - String password = "baeldung"; - String encoded = encoder.encode(password); - boolean actualResult = encoder.matches(password, encoded); - - assertThat(actualResult, is(true)); - } - - @Test - public void givenIncorrectPassword_whenMatchesIsCalled_thenReturnFalse() { - boolean actualResult = encoder.matches("baeldung", "spring"); - - assertThat(actualResult, is(false)); - } -} \ No newline at end of file