Spring Security Custom Authentication and Password Encoding
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.
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.
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
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>
精彩评论