diff --git a/config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java index 9ac1336857..11dc6c4c7c 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.java @@ -289,6 +289,11 @@ public class AuthenticationConfiguration { return getPasswordEncoder().matches(rawPassword, encodedPassword); } + @Override + public boolean upgradeEncoding(String encodedPassword) { + return getPasswordEncoder().upgradeEncoding(encodedPassword); + } + private PasswordEncoder getPasswordEncoder() { if (this.passwordEncoder != null) { return this.passwordEncoder; diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.java b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.java index c75b94ccb5..d8c14ff9cc 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebSecurityConfigurerAdapter.java @@ -593,6 +593,11 @@ public abstract class WebSecurityConfigurerAdapter implements return getPasswordEncoder().matches(rawPassword, encodedPassword); } + @Override + public boolean upgradeEncoding(String encodedPassword) { + return getPasswordEncoder().upgradeEncoding(encodedPassword); + } + private PasswordEncoder getPasswordEncoder() { if (this.passwordEncoder != null) { return this.passwordEncoder; diff --git a/crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java b/crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java index b6738db848..8f68bb80fb 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java +++ b/crypto/src/main/java/org/springframework/security/crypto/password/DelegatingPasswordEncoder.java @@ -216,6 +216,12 @@ public class DelegatingPasswordEncoder implements PasswordEncoder { return prefixEncodedPassword.substring(start + 1, end); } + @Override + public boolean upgradeEncoding(String encodedPassword) { + String id = extractId(encodedPassword); + return !this.idForEncode.equalsIgnoreCase(id); + } + private String extractEncodedPassword(String prefixEncodedPassword) { int start = prefixEncodedPassword.indexOf(SUFFIX); return prefixEncodedPassword.substring(start + 1); diff --git a/crypto/src/main/java/org/springframework/security/crypto/password/PasswordEncoder.java b/crypto/src/main/java/org/springframework/security/crypto/password/PasswordEncoder.java index 2b31acd8c4..4e77d3b19b 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/password/PasswordEncoder.java +++ b/crypto/src/main/java/org/springframework/security/crypto/password/PasswordEncoder.java @@ -42,4 +42,14 @@ public interface PasswordEncoder { */ boolean matches(CharSequence rawPassword, String encodedPassword); + /** + * Returns true if the encoded password should be encoded again for better security, + * else false. The default implementation always returns false. + * @param encodedPassword the encoded password to check + * @return true if the encoded password should be encoded again for better security, + * else false. + */ + default boolean upgradeEncoding(String encodedPassword) { + return false; + } } diff --git a/crypto/src/test/java/org/springframework/security/crypto/password/DelegatingPasswordEncoderTests.java b/crypto/src/test/java/org/springframework/security/crypto/password/DelegatingPasswordEncoderTests.java index d5deea8004..59a88de50f 100644 --- a/crypto/src/test/java/org/springframework/security/crypto/password/DelegatingPasswordEncoderTests.java +++ b/crypto/src/test/java/org/springframework/security/crypto/password/DelegatingPasswordEncoderTests.java @@ -198,4 +198,29 @@ public class DelegatingPasswordEncoderTests { public void matchesWhenRawPasswordNotNullAndEncodedPasswordNullThenThrowsIllegalArgumentException() { this.passwordEncoder.matches(this.rawPassword, null); } + + @Test + public void upgradeEncodingWhenEncodedPasswordNullThenTrue() { + assertThat(this.passwordEncoder.upgradeEncoding(null)).isTrue(); + } + + @Test + public void upgradeEncodingWhenNullIdThenTrue() { + assertThat(this.passwordEncoder.upgradeEncoding(this.encodedPassword)).isTrue(); + } + + @Test + public void upgradeEncodingWhenIdInvalidFormatThenTrue() { + assertThat(this.passwordEncoder.upgradeEncoding("{bcrypt"+ this.encodedPassword)).isTrue(); + } + + @Test + public void upgradeEncodingWhenSameIdThenFalse() { + assertThat(this.passwordEncoder.upgradeEncoding(this.bcryptEncodedPassword)).isFalse(); + } + + @Test + public void upgradeEncodingWhenDifferentIdThenTrue() { + assertThat(this.passwordEncoder.upgradeEncoding(this.noopEncodedPassword)).isTrue(); + } }