Spring Security Custom Authentication and Password Encoding

AuthenticationPasswordsSpring SecuritySalt

Authentication Problem Overview


Is there a tutorial out there or does anyone have pointers on how to do the following with Spring-Security?

Task:

I need to get the salt from my database for the authenticating username and use it to encrypt the provided password (from the login page) to compare it to the stored encrypted password (a.k.a. authenticate the user).

additional information:

I use a custom database structure. A UserDetails object is created via a custom UserDetailsService which in turn uses a custom DAOProvider to get the information from the database.

my security.xml file so far:

<authentication-manager>
	<authentication-provider user-service-ref="userDetailsService">
	</authentication-provider>
</authentication-manager>

now I guess I'll need

		<password-encoder hash="sha" />

but what else? How do I tell spring security to use the databaseprovided salt in order to encode the password?


edit:

I found This SO post to be informatative but not sufficient: If I define a salt source in my xml to be used by the password encoder, like so:

		<password-encoder ref="passwordEncoder">                
        	<salt-source ref="saltSource"/>
        </password-encoder>

I'll have to write a custom SaltSource to use my custom salt. But that's not to be found inside the UserDetails object. So...

Alternative 1:

Can I use a custom Implementation of UserDetails which might then have the salt property?

<beans:bean id="saltSource" class="path.to.MySaltSource"
    p:userPropertyToUse="salt"/>

and

@Service("userDetailsService") 
public class UserDetailsServiceImpl implements UserDetailsService {
	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException, DataAccessException {

		// ...
		return buildUserFromAccount(account);
	}

	@Transactional(readOnly = true)

	UserDetailsImpl buildUserFromAccount(Account account){

		// ... build User object that contains salt property
}

custom User Class:

public class UserDetailsImpl extends User{

	// ...
	
	private String salt;

	public String getSalt() { return salt; }

	public void setSalt(String salt) { this.salt = salt; }
}

security.xml:

<authentication-manager>
	<authentication-provider user-service-ref="userDetailsService">
		<password-encoder hash="sha">                
    	<salt-source ref="saltSource"/>
    </password-encoder>
	</authentication-provider>
</authentication-manager>

<beans:bean id="saltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource" p:userPropertyToUse="salt"/>


Alternative 2:

Otherwise I'd have to inject my accountDAO into the SaltSource to extract the salt for a given userName from the database.

BUT: How does Spring Security call the SaltSource? Always with saltSource.getSalt(userDetails)?

Then I'd just have to make sure my SaltSource uses userDetails.accountName on my accountDAO to retrieve the salt.


Edit2:

Just learned that my approach is.. legacy.. :( So I guess I'll just use the StandardPasswordEncoder (which I still have to figure out how to use exactly).

BTW: I implemented the first option with a custom UserDetails class extending the User class and just adding a salt property which an then be passed to the SaltSource as a userPropertyToUse just like it has been proposed in the SO post mentioned in Edit 1...


EDIT 3:

Just got the StandardPasswordEncoder working, so I'll leave some pointers here:

Use the StandardPasswordEncoder for Authentication:

<beans:bean id="encoder" 
	class="org.springframework.security.crypto.password.StandardPasswordEncoder">
</beans:bean>


<authentication-manager>
	<authentication-provider user-service-ref="userDetailsService">
		<password-encoder ref="encoder" />         
	</authentication-provider>
</authentication-manager>

This requires the spring-security-crypto module in version 3.1.0.RC? as far as I know. Couldn't find any repository that has a 3.0. version (even though somewhere it had the versions listed that included 3.0.6 and so on). Also the documentations talks about spring security 3.1 so I figured, I'll just go with that.

When creating a user (for me only an admin can do that), I just use

		StandardPasswordEncoder encoder = new StandardPasswordEncoder();
		String result = encoder.encode(password);

and I'm done.

Spring security will randomly create a salt and add it to the password string before storing it in the database so no salt column is needed anymore.

One can however also provide a global salt as a constructor argument (new StandardPasswordEncoder("12345");), but I didn't know how to set up my security configuration to retrieve that value from a bean instead of supplying a static string with <constructor-arg name="secret" value "12345" />. But I don't know how much that is needed anyway.

Authentication Solutions


Solution 1 - Authentication

I'll mark this as answered, as I solved my problem and no other comments or answers were given:

Edit 1 - Alternative 1 answers the original question

BUT I had to learn that customized password salting is a legacy approach that is not needed in Spring Security 3.1 any more, as I describe in

Edit 3 where I left some pointers on how to use the StandardPasswordEncoder for automated Salts that are stored with the password.

Solution 2 - Authentication

For what it's worth, I wrote this blog post detailing what you've described: http://rtimothy.tumblr.com/post/26527448708/spring-3-1-security-and-salting-passwords

Solution 3 - Authentication

To retrieve the global salt from a bean, use the Spring Expression Language or SpEL.

<beans:bean id="encoder" 
        class="org.springframework.security.crypto.password.StandardPasswordEncoder">
    <constructor-arg value="#{someotherBean.somePropertyWithAGetterMethod"/>
</beans:bean>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPeteView Question on Stackoverflow
Solution 1 - AuthenticationPeteView Answer on Stackoverflow
Solution 2 - AuthenticationrooftopView Answer on Stackoverflow
Solution 3 - AuthenticationpaulchapmanView Answer on Stackoverflow