Remove SaltSource

Fixes gh-4681
This commit is contained in:
Rob Winch 2017-10-22 20:55:56 -05:00
parent d832213c6c
commit cdc992b132
16 changed files with 2 additions and 670 deletions

View File

@ -55,7 +55,6 @@ public abstract class Elements {
public static final String FILTER_CHAIN = "filter-chain";
public static final String GLOBAL_METHOD_SECURITY = "global-method-security";
public static final String PASSWORD_ENCODER = "password-encoder";
public static final String SALT_SOURCE = "salt-source";
public static final String PORT_MAPPINGS = "port-mappings";
public static final String PORT_MAPPING = "port-mapping";
public static final String CUSTOM_FILTER = "custom-filter";

View File

@ -47,11 +47,6 @@ public class AuthenticationProviderBeanDefinitionParser implements BeanDefinitio
PasswordEncoderParser pep = new PasswordEncoderParser(passwordEncoderElt, pc);
authProvider.getPropertyValues().addPropertyValue("passwordEncoder",
pep.getPasswordEncoder());
if (pep.getSaltSource() != null) {
authProvider.getPropertyValues().addPropertyValue("saltSource",
pep.getSaltSource());
}
}
Element userServiceElt = DomUtils.getChildElementByTagName(element,

View File

@ -14,7 +14,6 @@
* limitations under the License.
*/
package org.springframework.security.config.authentication;
import java.util.HashMap;
import java.util.Map;
@ -35,8 +34,6 @@ import org.w3c.dom.Element;
/**
* Stateful parser for the <password-encoder> element.
*
* Will produce a PasswordEncoder and (optionally) a SaltSource.
*
* @author Luke Taylor
*/
public class PasswordEncoderParser {
@ -55,7 +52,6 @@ public class PasswordEncoderParser {
private static final Log logger = LogFactory.getLog(PasswordEncoderParser.class);
private BeanMetadataElement passwordEncoder;
private BeanMetadataElement saltSource;
public PasswordEncoderParser(Element element, ParserContext parserContext) {
parse(element, parserContext);
@ -79,21 +75,6 @@ public class PasswordEncoderParser {
((RootBeanDefinition) passwordEncoder).setSource(parserContext
.extractSource(element));
}
Element saltSourceElt = DomUtils.getChildElementByTagName(element,
Elements.SALT_SOURCE);
if (saltSourceElt != null) {
if (OPT_HASH_BCRYPT.equals(hash)) {
parserContext.getReaderContext().error(
Elements.SALT_SOURCE + " isn't compatible with bcrypt",
parserContext.extractSource(saltSourceElt));
}
else {
saltSource = new SaltSourceBeanDefinitionParser().parse(saltSourceElt,
parserContext);
}
}
}
public static BeanDefinition createPasswordEncoderBeanDefinition(String hash,
@ -107,8 +88,4 @@ public class PasswordEncoderParser {
public BeanMetadataElement getPasswordEncoder() {
return passwordEncoder;
}
public BeanMetadataElement getSaltSource() {
return saltSource;
}
}

View File

@ -1,74 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* 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.config.authentication;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.authentication.dao.ReflectionSaltSource;
import org.springframework.security.authentication.dao.SystemWideSaltSource;
import org.springframework.security.config.Elements;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* @author Luke Taylor
* @since 2.0
*/
class SaltSourceBeanDefinitionParser {
private static final String ATT_USER_PROPERTY = "user-property";
private static final String ATT_REF = "ref";
private static final String ATT_SYSTEM_WIDE = "system-wide";
public BeanMetadataElement parse(Element element, ParserContext parserContext) {
String ref = element.getAttribute(ATT_REF);
if (StringUtils.hasText(ref)) {
return new RuntimeBeanReference(ref);
}
String userProperty = element.getAttribute(ATT_USER_PROPERTY);
RootBeanDefinition saltSource;
if (StringUtils.hasText(userProperty)) {
saltSource = new RootBeanDefinition(ReflectionSaltSource.class);
saltSource.getPropertyValues().addPropertyValue("userPropertyToUse",
userProperty);
saltSource.setSource(parserContext.extractSource(element));
saltSource.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
return saltSource;
}
String systemWideSalt = element.getAttribute(ATT_SYSTEM_WIDE);
if (StringUtils.hasText(systemWideSalt)) {
saltSource = new RootBeanDefinition(SystemWideSaltSource.class);
saltSource.getPropertyValues().addPropertyValue("systemWideSalt",
systemWideSalt);
saltSource.setSource(parserContext.extractSource(element));
saltSource.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
return saltSource;
}
parserContext.getReaderContext().error(
Elements.SALT_SOURCE + " requires an attribute", element);
return null;
}
}

View File

@ -103,12 +103,6 @@ public class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {
passwordEncoderElement, parserContext);
authenticatorBuilder.addPropertyValue("passwordEncoder",
pep.getPasswordEncoder());
if (pep.getSaltSource() != null) {
parserContext.getReaderContext().warning(
"Salt source information isn't valid when used with LDAP",
passwordEncoderElement);
}
}
else if (StringUtils.hasText(hash)) {
authenticatorBuilder.addPropertyValue("passwordEncoder",

View File

@ -54,20 +54,10 @@ debug =
password-encoder =
## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example.
element password-encoder {password-encoder.attlist, salt-source?}
element password-encoder {password-encoder.attlist}
password-encoder.attlist &=
ref | (hash)
salt-source =
## Password salting strategy. A system-wide constant or a property from the UserDetails object can be used.
element salt-source {user-property | system-wide | ref}
user-property =
## A property of the UserDetails object which will be used as salt by a password encoder. Typically something like "username" might be used.
attribute user-property {xsd:token}
system-wide =
## A single value that will be used as the salt for a password encoder.
attribute system-wide {xsd:token}
role-prefix =
## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. "ROLE_"). Use the value "none" for no prefix in cases where the default is non-empty.
attribute role-prefix {xsd:token}

View File

@ -144,24 +144,6 @@
</xs:simpleType>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="user-property">
<xs:attribute name="user-property" use="required" type="xs:token">
<xs:annotation>
<xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.
Typically something like "username" might be used.
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="system-wide">
<xs:attribute name="system-wide" use="required" type="xs:token">
<xs:annotation>
<xs:documentation>A single value that will be used as the salt for a password encoder.
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:attributeGroup name="role-prefix">
<xs:attribute name="role-prefix" use="required" type="xs:token">
<xs:annotation>
@ -2007,36 +1989,6 @@
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="salt-source">
<xs:annotation>
<xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails
object can be used.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="user-property" type="xs:token">
<xs:annotation>
<xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.
Typically something like "username" might be used.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="system-wide" type="xs:token">
<xs:annotation>
<xs:documentation>A single value that will be used as the salt for a password encoder.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ref" type="xs:token">
<xs:annotation>
<xs:documentation>Defines a reference to a Spring bean Id.
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attributeGroup ref="security:password-encoder.attlist"/>
</xs:complexType>
</xs:element>
@ -2066,36 +2018,6 @@
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="salt-source">
<xs:annotation>
<xs:documentation>Password salting strategy. A system-wide constant or a property from the UserDetails
object can be used.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attribute name="user-property" type="xs:token">
<xs:annotation>
<xs:documentation>A property of the UserDetails object which will be used as salt by a password encoder.
Typically something like "username" might be used.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="system-wide" type="xs:token">
<xs:annotation>
<xs:documentation>A single value that will be used as the salt for a password encoder.
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ref" type="xs:token">
<xs:annotation>
<xs:documentation>Defines a reference to a Spring bean Id.
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attributeGroup ref="security:password-encoder.attlist"/>
</xs:complexType>
</xs:element>

View File

@ -18,7 +18,6 @@ package org.springframework.security.config.authentication;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.ReflectionSaltSource;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
import org.springframework.security.crypto.password.LdapShaPasswordEncoder;
@ -80,25 +79,6 @@ public class AuthenticationProviderBeanDefinitionParserTests {
getProvider().authenticate(bob);
}
@Test(expected = BeanDefinitionParsingException.class)
public void bCryptAndSaltSourceRaisesException() throws Exception {
appContext = new InMemoryXmlApplicationContext(
""
+ " <authentication-manager>"
+ " <authentication-provider>"
+ " <password-encoder hash='bcrypt'>"
+ " <salt-source ref='saltSource'/>"
+ " </password-encoder>"
+ " <user-service>"
+ " <user name='bob' password='$2a$05$dRmjl1T05J7rvCPD2NgsHesCEJHww3pdmesUhjM3PD4m/gaEYyx/G' authorities='ROLE_A' />"
+ " </user-service>" + " </authentication-provider>"
+ " </authentication-manager>"
+ " <b:bean id='saltSource' class='"
+ ReflectionSaltSource.class.getName() + "'>"
+ " <b:property name='userPropertyToUse' value='username'/>"
+ " </b:bean>");
}
@Test
public void providerWithMd5PasswordEncoderWorks() throws Exception {
appContext = new InMemoryXmlApplicationContext(

View File

@ -60,8 +60,6 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
*/
private String userNotFoundEncodedPassword;
private SaltSource saltSource;
private UserDetailsService userDetailsService;
public DaoAuthenticationProvider() {
@ -75,12 +73,6 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
Object salt = null;
if (this.saltSource != null) {
salt = this.saltSource.getSalt(userDetails);
}
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
@ -155,26 +147,6 @@ public class DaoAuthenticationProvider extends AbstractUserDetailsAuthentication
return passwordEncoder;
}
/**
* The source of salts to use when decoding passwords. <code>null</code> is a valid
* value, meaning the <code>DaoAuthenticationProvider</code> will present
* <code>null</code> to the relevant <code>PasswordEncoder</code>.
* <p>
* 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
* <code>PasswordEncoder</code>
*/
public void setSaltSource(SaltSource saltSource) {
this.saltSource = saltSource;
}
protected SaltSource getSaltSource() {
return saltSource;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}

View File

@ -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.
* <p>
* This allows you to subclass <code>User</code> 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 <code>username</code> 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 <code>User</code> to obtain the salt.
* <p>
* The property identified by <code>userPropertyToUse</code> must be available from
* the passed <code>User</code> object. If it is not available, an
* {@link AuthenticationServiceException} will be thrown.
*
* @param user which contains the method identified by <code>userPropertyToUse</code>
*
* @return the result of invoking <tt>user.userPropertyToUse()</tt>, or if the method
* doesn't exist, <tt>user.getUserPropertyToUse()</tt>.
*
* @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 <code>UserDetails</code> contains a
* <code>UserDetails.getSalt()</code> method, you should set this property to
* "getSalt" or "salt".
*
* @param userPropertyToUse the name of the <b>getter</b> to call to obtain the salt
* from the <code>UserDetails</code>
*/
public void setUserPropertyToUse(String userPropertyToUse) {
this.userPropertyToUse = userPropertyToUse;
}
public String toString() {
return "ReflectionSaltSource[ userPropertyToUse='" + userPropertyToUse + "'; ]";
}
}

View File

@ -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 <code>AuthenticationDao</code>
*
* @return the salt to use for this <code>UserDetails</code>
*/
Object getSalt(UserDetails user);
}

View File

@ -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 <code>String</code> as the salt.
* <P>
* 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.
* </p>
*
* @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;
}
}

View File

@ -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);

View File

@ -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");
}
}

View File

@ -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);
}
}

View File

@ -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]]
==== <password-encoder>
Authentication providers can optionally be configured to use a password encoder as described in the <<ns-password-encoder,namespace introduction>>. This will result in the bean being injected with the appropriate `PasswordEncoder` instance, potentially with an accompanying `SaltSource` bean to provide salt values for hashing.
Authentication providers can optionally be configured to use a password encoder as described in the <<ns-password-encoder,namespace introduction>>. This will result in the bean being injected with the appropriate `PasswordEncoder` instance.
[[nsa-password-encoder-parents]]
@ -9195,46 +9190,6 @@ Defines the hashing algorithm used on user passwords. We recommend strongly agai
Defines a reference to a Spring bean that implements `PasswordEncoder`.
[[nsa-password-encoder-children]]
===== Child Elements of <password-encoder>
* <<nsa-salt-source,salt-source>>
[[nsa-salt-source]]
==== <salt-source>
Password salting strategy. A system-wide constant or a property from the UserDetails object can be used.
[[nsa-salt-source-parents]]
===== Parent Elements of <salt-source>
* <<nsa-password-encoder,password-encoder>>
[[nsa-salt-source-attributes]]
===== <salt-source> Attributes
[[nsa-salt-source-ref]]
* **ref**
Defines a reference to a Spring bean Id.
[[nsa-salt-source-system-wide]]
* **system-wide**
A single value that will be used as the salt for a password encoder.
[[nsa-salt-source-user-property]]
* **user-property**
A property of the UserDetails object which will be used as salt by a password encoder. Typically something like "username" might be used.
[[nsa-user-service]]
==== <user-service>
Creates an in-memory UserDetailsService from a properties file or a list of "user" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required.