null
is a valid
- * value, meaning the DaoAuthenticationProvider
will present
- * null
to the relevant PasswordEncoder
.
- *
- * Instead, it is recommended that you use an encoder which uses a random salt and
- * combines it with the password field. This is the default approach taken in the
- * {@code org.springframework.security.crypto.password} package.
- *
- * @param saltSource to use when attempting to decode passwords via the
- * PasswordEncoder
- */
- public void setSaltSource(SaltSource saltSource) {
- this.saltSource = saltSource;
- }
-
- protected SaltSource getSaltSource() {
- return saltSource;
- }
-
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
diff --git a/core/src/main/java/org/springframework/security/authentication/dao/ReflectionSaltSource.java b/core/src/main/java/org/springframework/security/authentication/dao/ReflectionSaltSource.java
deleted file mode 100644
index fa1cfc0690..0000000000
--- a/core/src/main/java/org/springframework/security/authentication/dao/ReflectionSaltSource.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.security.authentication.dao;
-
-import org.springframework.security.authentication.AuthenticationServiceException;
-import org.springframework.security.core.userdetails.UserDetails;
-
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.beans.BeanUtils;
-import org.springframework.util.ReflectionUtils;
-import org.springframework.util.Assert;
-
-import java.lang.reflect.Method;
-import java.beans.PropertyDescriptor;
-
-/**
- * Obtains a salt from a specified property of the
- * {@link org.springframework.security.core.userdetails.User} object.
- *
- * This allows you to subclass User
and provide an additional bean getter for
- * a salt. You should use a synthetic value that does not change, such as a database
- * primary key. Do not use username
if it is likely to change.
- *
- * @author Ben Alex
- */
-public class ReflectionSaltSource implements SaltSource, InitializingBean {
- // ~ Instance fields
- // ================================================================================================
-
- private String userPropertyToUse;
-
- // ~ Methods
- // ========================================================================================================
-
- public void afterPropertiesSet() throws Exception {
- Assert.hasText(userPropertyToUse, "A userPropertyToUse must be set");
- }
-
- /**
- * Performs reflection on the passed User
to obtain the salt.
- *
- * The property identified by userPropertyToUse
must be available from
- * the passed User
object. If it is not available, an
- * {@link AuthenticationServiceException} will be thrown.
- *
- * @param user which contains the method identified by userPropertyToUse
- *
- * @return the result of invoking user.userPropertyToUse(), or if the method
- * doesn't exist, user.getUserPropertyToUse().
- *
- * @throws AuthenticationServiceException if reflection fails
- */
- public Object getSalt(UserDetails user) {
- Method saltMethod = findSaltMethod(user);
-
- try {
- return saltMethod.invoke(user);
- }
- catch (Exception exception) {
- throw new AuthenticationServiceException(exception.getMessage(), exception);
- }
- }
-
- private Method findSaltMethod(UserDetails user) {
- Method saltMethod = ReflectionUtils.findMethod(user.getClass(),
- userPropertyToUse, new Class[0]);
-
- if (saltMethod == null) {
- PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(user.getClass(),
- userPropertyToUse);
-
- if (pd != null) {
- saltMethod = pd.getReadMethod();
- }
-
- if (saltMethod == null) {
- throw new AuthenticationServiceException(
- "Unable to find salt method on user Object. Does the class '"
- + user.getClass().getName()
- + "' have a method or getter named '" + userPropertyToUse
- + "' ?");
- }
- }
-
- return saltMethod;
- }
-
- protected String getUserPropertyToUse() {
- return userPropertyToUse;
- }
-
- /**
- * The method name to call to obtain the salt. Can be either a method name or a bean
- * property name. If your UserDetails
contains a
- * UserDetails.getSalt()
method, you should set this property to
- * "getSalt" or "salt".
- *
- * @param userPropertyToUse the name of the getter to call to obtain the salt
- * from the UserDetails
- */
- public void setUserPropertyToUse(String userPropertyToUse) {
- this.userPropertyToUse = userPropertyToUse;
- }
-
- public String toString() {
- return "ReflectionSaltSource[ userPropertyToUse='" + userPropertyToUse + "'; ]";
- }
-}
diff --git a/core/src/main/java/org/springframework/security/authentication/dao/SaltSource.java b/core/src/main/java/org/springframework/security/authentication/dao/SaltSource.java
deleted file mode 100644
index a800a36844..0000000000
--- a/core/src/main/java/org/springframework/security/authentication/dao/SaltSource.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.security.authentication.dao;
-
-import org.springframework.security.core.userdetails.UserDetails;
-
-/**
- * Provides alternative sources of the salt to use for encoding passwords.
- *
- * @author Ben Alex
- */
-public interface SaltSource {
- // ~ Methods
- // ========================================================================================================
-
- /**
- * Returns the salt to use for the indicated user.
- *
- * @param user from the AuthenticationDao
- *
- * @return the salt to use for this UserDetails
- */
- Object getSalt(UserDetails user);
-}
diff --git a/core/src/main/java/org/springframework/security/authentication/dao/SystemWideSaltSource.java b/core/src/main/java/org/springframework/security/authentication/dao/SystemWideSaltSource.java
deleted file mode 100644
index cea85fbb77..0000000000
--- a/core/src/main/java/org/springframework/security/authentication/dao/SystemWideSaltSource.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.security.authentication.dao;
-
-import org.springframework.security.core.userdetails.UserDetails;
-
-import org.springframework.beans.factory.InitializingBean;
-
-/**
- * Uses a static system-wide String
as the salt.
- *
- * Does not supply a different salt for each - * {@link org.springframework.security.core.userdetails.User}. This means users sharing - * the same password will still have the same digested password. Of benefit is the - * digested passwords will at least be more protected than if stored without any salt. - *
- * - * @author Ben Alex - */ -public class SystemWideSaltSource implements SaltSource, InitializingBean { - // ~ Instance fields - // ================================================================================================ - - private String systemWideSalt; - - // ~ Methods - // ======================================================================================================== - - public void afterPropertiesSet() throws Exception { - if ((this.systemWideSalt == null) || "".equals(this.systemWideSalt)) { - throw new IllegalArgumentException("A systemWideSalt must be set"); - } - } - - public Object getSalt(UserDetails user) { - return this.systemWideSalt; - } - - public String getSystemWideSalt() { - return this.systemWideSalt; - } - - public void setSystemWideSalt(String systemWideSalt) { - this.systemWideSalt = systemWideSalt; - } - - /** - * Displays the system wide salt - * @since 4.0 - * @return - */ - @Override - public String toString() { - return systemWideSalt; - } -} diff --git a/core/src/test/java/org/springframework/security/authentication/dao/DaoAuthenticationProviderTests.java b/core/src/test/java/org/springframework/security/authentication/dao/DaoAuthenticationProviderTests.java index bc360853af..7a6dc2c875 100644 --- a/core/src/test/java/org/springframework/security/authentication/dao/DaoAuthenticationProviderTests.java +++ b/core/src/test/java/org/springframework/security/authentication/dao/DaoAuthenticationProviderTests.java @@ -394,10 +394,6 @@ public class DaoAuthenticationProviderTests { assertThat(provider.getPasswordEncoder().getClass()).isEqualTo( BCryptPasswordEncoder.class); - provider.setSaltSource(new SystemWideSaltSource()); - assertThat(provider.getSaltSource().getClass()).isEqualTo( - SystemWideSaltSource.class); - provider.setUserCache(new EhCacheBasedUserCache()); assertThat(provider.getUserCache().getClass()).isEqualTo( EhCacheBasedUserCache.class); diff --git a/core/src/test/java/org/springframework/security/authentication/dao/salt/ReflectionSaltSourceTests.java b/core/src/test/java/org/springframework/security/authentication/dao/salt/ReflectionSaltSourceTests.java deleted file mode 100644 index 42be12644d..0000000000 --- a/core/src/test/java/org/springframework/security/authentication/dao/salt/ReflectionSaltSourceTests.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.authentication.dao.salt; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.Test; -import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.authentication.dao.ReflectionSaltSource; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; - -/** - * Tests {@link ReflectionSaltSource}. - * - * @author Ben Alex - */ -public class ReflectionSaltSourceTests { - private UserDetails user = new User("scott", "wombat", true, true, true, true, - AuthorityUtils.createAuthorityList("HOLDER")); - - // ~ Methods - // ======================================================================================================== - - @Test(expected = IllegalArgumentException.class) - public void detectsMissingUserPropertyToUse() throws Exception { - ReflectionSaltSource saltSource = new ReflectionSaltSource(); - saltSource.afterPropertiesSet(); - } - - @Test(expected = AuthenticationServiceException.class) - public void exceptionIsThrownWhenInvalidPropertyRequested() throws Exception { - ReflectionSaltSource saltSource = new ReflectionSaltSource(); - saltSource.setUserPropertyToUse("getDoesNotExist"); - saltSource.afterPropertiesSet(); - saltSource.getSalt(user); - } - - @Test - public void methodNameAsPropertyToUseReturnsCorrectSaltValue() { - ReflectionSaltSource saltSource = new ReflectionSaltSource(); - saltSource.setUserPropertyToUse("getUsername"); - - assertThat(saltSource.getSalt(user)).isEqualTo("scott"); - } - - @Test - public void propertyNameAsPropertyToUseReturnsCorrectSaltValue() { - ReflectionSaltSource saltSource = new ReflectionSaltSource(); - saltSource.setUserPropertyToUse("password"); - assertThat(saltSource.getSalt(user)).isEqualTo("wombat"); - } -} diff --git a/core/src/test/java/org/springframework/security/authentication/dao/salt/SystemWideSaltSourceTests.java b/core/src/test/java/org/springframework/security/authentication/dao/salt/SystemWideSaltSourceTests.java deleted file mode 100644 index 73e281e004..0000000000 --- a/core/src/test/java/org/springframework/security/authentication/dao/salt/SystemWideSaltSourceTests.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2004, 2005, 2006 Acegi Technology Pty Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.authentication.dao.salt; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; - -import org.junit.Test; -import org.springframework.security.authentication.dao.SystemWideSaltSource; - -/** - * Tests {@link SystemWideSaltSource}. - * - * @author Ben Alex - */ -public class SystemWideSaltSourceTests { - // ~ Constructors - // =================================================================================================== - - public SystemWideSaltSourceTests() { - super(); - } - - // ~ Methods - // ======================================================================================================== - @Test - public void testDetectsMissingSystemWideSalt() throws Exception { - SystemWideSaltSource saltSource = new SystemWideSaltSource(); - - try { - saltSource.afterPropertiesSet(); - fail("Should have thrown IllegalArgumentException"); - } - catch (IllegalArgumentException expected) { - assertThat(expected.getMessage()).isEqualTo("A systemWideSalt must be set"); - } - } - - @Test - public void testGettersSetters() { - SystemWideSaltSource saltSource = new SystemWideSaltSource(); - saltSource.setSystemWideSalt("helloWorld"); - assertThat(saltSource.getSystemWideSalt()).isEqualTo("helloWorld"); - } - - @Test - public void testNormalOperation() throws Exception { - SystemWideSaltSource saltSource = new SystemWideSaltSource(); - saltSource.setSystemWideSalt("helloWorld"); - saltSource.afterPropertiesSet(); - assertThat(saltSource.getSalt(null)).isEqualTo("helloWorld"); - } - - // SEC-2173 - @Test - public void testToString() { - String systemWideSalt = "helloWorld"; - SystemWideSaltSource saltSource = new SystemWideSaltSource(); - saltSource.setSystemWideSalt(systemWideSalt); - assertThat(saltSource.toString()).isEqualTo(systemWideSalt); - } -} diff --git a/docs/manual/src/docs/asciidoc/index.adoc b/docs/manual/src/docs/asciidoc/index.adoc index df8d3d9416..728947ba5c 100644 --- a/docs/manual/src/docs/asciidoc/index.adoc +++ b/docs/manual/src/docs/asciidoc/index.adoc @@ -2515,11 +2515,6 @@ One potential problem with the use of password hashes that it is relatively easy Bcrypt automatically generates a random salt value for each password when it is encoded, and stores it in the bcrypt string in a standard format. -[NOTE] -==== -The legacy approach to handling salt was to inject a `SaltSource` into the `DaoAuthenticationProvider`, which would obtain a salt value for a particular user and pass it to the `PasswordEncoder`. Using bcrypt means you don't have worry about the details of salt handling (such as where the value is stored), as it is all done internally. So we'd strongly recommend you use bcrypt unless you already have a system in place which stores the salt separately. -==== - ==== Hashing and Authentication When an authentication provider (such as Spring Security's `DaoAuthenticationProvider`) needs to check the password in a submitted authentication request against the known value for a user, and the stored password is encoded in some way, then the submitted value must be encoded using exactly the same algorithm. It's up to you to check that these are compatible as Spring Security has no control over the persistent values. If you add password hashing to your authentication configuration in Spring Security, and your database contains plaintext passwords, then there is no way authentication can succeed. Even if you are aware that your database is using MD5 to encode the passwords, for example, and your application is configured to use Spring Security's `Md5PasswordEncoder`, there are still things that can go wrong. The database may have the passwords encoded in Base 64, for example while the encoder is using hexadecimal strings (the default). Alternatively your database may be using upper-case while the output from the encoder is lower-case. Make sure you write a test to check the output from your configured password encoder with a known password and salt combination and check that it matches the database value before going further and attempting to authenticate through your application. Using a standard like bcrypt will avoid these issues. @@ -9169,7 +9164,7 @@ select username, password, enabled from users where username = ? [[nsa-password-encoder]] ====