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.