I currently log users in programmatically (like when they login through Facebook or other means than using my login form) with:
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(user, "", authorities)
);
What I want to do instead is log the user in as if they set the remember-me option on in the login form. So I'm guessing I need to use the RememberMeAuthenticationToken
instead of the UsernamePasswordAuthenticationToken
? But what do I put for the key
argument of the constructor?
RememberMeAuthenticationToken(String key, Object principal, Collection<? extends GrantedAuthority> authorities)
UPDATE: I'm using the Persistent Token Approach described here. So there is no key like in the Simple Hash-Based Token Approach.
I assume you already have <remember-me>
set in your configuration.
The way remember-me works is it sets a cookie that is recognized when the user comes back to the site after their session has expired.
You'll have to subclass the RememberMeServices
(TokenBased
or PersistentTokenBased
) you are using and make the onLoginSuccess() public. For example:
public class MyTokenBasedRememberMeServices extends PersistentTokenBasedRememberMeServices {
@Override
public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
super.onLoginSuccess(request, response, successfulAuthentication);
}
}
<remember-me services-ref="rememberMeServices"/>
<bean id="rememberMeServices" class="foo.MyTokenBasedRememberMeServices">
<property name="userDetailsService" ref="myUserDetailsService"/>
<!-- etc -->
</bean>
Inject your RememberMeServices into the bean where you are doing the programmatic login. Then call onLoginSuccess()
on it, using the UsernamePasswordAuthenticationToken that you created. That will set the cookie.
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, "", authorities);
SecurityContextHolder.getContext().setAuthentication(auth);
getRememberMeServices().onLoginSuccess(request, response, auth);
UPDATE
@at improved upon this, with no subclassing of RememberMeServices:
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, "", authorities);
SecurityContextHolder.getContext().setAuthentication(auth);
// This wrapper is important, it causes the RememberMeService to see
// "true" for the "_spring_security_remember_me" parameter.
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
@Override public String getParameter(String name) { return "true"; }
};
getRememberMeServices().loginSuccess(wrapper, response, auth);
This is the source for the constructor.
public RememberMeAuthenticationToken(String key, Object principal, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
if ((key == null) || ("".equals(key)) || (principal == null) || "".equals(principal)) {
throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
}
this.keyHash = key.hashCode();
this.principal = principal;
setAuthenticated(true);
}
The key is hashed and its used to determine whether the authentication used for this user in the security context is not a 'forged' one.
Have a look at the RememberMeAuthenicationProvider
source.
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (!supports(authentication.getClass())) {
return null;
}
if (this.key.hashCode() != ((RememberMeAuthenticationToken) authentication).getKeyHash()) {
throw new BadCredentialsException(messages.getMessage("RememberMeAuthenticationProvider.incorrectKey",
"The presented RememberMeAuthenticationToken does not contain the expected key"));
}
return authentication;
}
So to answer your question, you need to pass the hash code of the key
field of the Authentication
representing the user.