mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-04 01:32:14 +00:00
SEC-565: Refactoring of TokenBasedRememberMeServices. Changed arguments to makeValidSignature so that it could be used from both places where a signature is required and refactored the class to extend AbstractRememberMeServices. The method processAutoLoginCookie now returns a UserDetails, rather than username, as the UserDetails is needed in TokenBasedRememberMeServices.
This commit is contained in:
parent
1a5ef2dece
commit
0e7dac6ca5
@ -29,19 +29,23 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler {
|
public abstract class AbstractRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler {
|
||||||
|
//~ Static fields/initializers =====================================================================================
|
||||||
|
|
||||||
|
public static final String SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY = "SPRING_SECURITY_REMEMBER_ME_COOKIE";
|
||||||
|
public static final String DEFAULT_PARAMETER = "_spring_security_remember_me";
|
||||||
|
|
||||||
|
private static final String DELIMITER = ":";
|
||||||
|
|
||||||
|
//~ Instance fields ================================================================================================
|
||||||
protected final Log logger = LogFactory.getLog(getClass());
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||||
|
|
||||||
public static final String DEFAULT_PARAMETER = "_spring_security_remember_me";
|
|
||||||
public static final String SPRING_SECURITY_PERSISTENT_REMEMBER_ME_COOKIE_KEY = "SPRING_SECURITY_REMEMBER_ME_COOKIE";
|
|
||||||
private static final String DELIMITER = ":";
|
|
||||||
|
|
||||||
private UserDetailsService userDetailsService;
|
private UserDetailsService userDetailsService;
|
||||||
private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
||||||
|
|
||||||
private String cookieName = SPRING_SECURITY_PERSISTENT_REMEMBER_ME_COOKIE_KEY;
|
private String cookieName = SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY;
|
||||||
private String parameter = DEFAULT_PARAMETER;
|
private String parameter = DEFAULT_PARAMETER;
|
||||||
private boolean alwaysRemember;
|
private boolean alwaysRemember;
|
||||||
private String key;
|
private String key;
|
||||||
@ -75,8 +79,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
String[] cookieTokens = decodeCookie(rememberMeCookie);
|
String[] cookieTokens = decodeCookie(rememberMeCookie);
|
||||||
String username = processAutoLoginCookie(cookieTokens, request, response);
|
user = processAutoLoginCookie(cookieTokens, request, response);
|
||||||
user = loadAndValidateUserDetails(username);
|
|
||||||
} catch (CookieTheftException cte) {
|
} catch (CookieTheftException cte) {
|
||||||
cancelCookie(request, response);
|
cancelCookie(request, response);
|
||||||
throw cte;
|
throw cte;
|
||||||
@ -172,22 +175,23 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UserDetails loadAndValidateUserDetails(String username) throws UsernameNotFoundException,
|
/**
|
||||||
|
* Provided for subclass convenience to check the account status of a loaded user.
|
||||||
|
*
|
||||||
|
* @throws UsernameNotFoundException if the username could not be located by the configured UserDetailsService.
|
||||||
|
* @throws RememberMeAuthenticationException if the account is locked or disabled.
|
||||||
|
*/
|
||||||
|
protected void validateUserDetails(UserDetails user) throws UsernameNotFoundException,
|
||||||
RememberMeAuthenticationException {
|
RememberMeAuthenticationException {
|
||||||
|
|
||||||
UserDetails user;
|
|
||||||
|
|
||||||
user = this.userDetailsService.loadUserByUsername(username);
|
|
||||||
|
|
||||||
if (!user.isAccountNonExpired() || !user.isCredentialsNonExpired() || !user.isEnabled()) {
|
if (!user.isAccountNonExpired() || !user.isCredentialsNonExpired() || !user.isEnabled()) {
|
||||||
throw new RememberMeAuthenticationException("Remember-me login was valid for user " +
|
throw new RememberMeAuthenticationException("Remember-me login was valid for user " +
|
||||||
user.getUsername() + ", but account is expired, has expired credentials or is disabled");
|
user.getUsername() + ", but account is expired, has expired credentials or is disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void loginFail(HttpServletRequest request, HttpServletResponse response) {
|
public final void loginFail(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
logger.debug("Interactive login attempt was unsuccessful.");
|
||||||
cancelCookie(request, response);
|
cancelCookie(request, response);
|
||||||
onLoginFail(request, response);
|
onLoginFail(request, response);
|
||||||
}
|
}
|
||||||
@ -202,6 +206,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
|
|||||||
Authentication successfulAuthentication) {
|
Authentication successfulAuthentication) {
|
||||||
|
|
||||||
if (!rememberMeRequested(request, parameter)) {
|
if (!rememberMeRequested(request, parameter)) {
|
||||||
|
logger.debug("Remember-me login not requested.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,12 +255,14 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
|
|||||||
* @param cookieTokens the decoded and tokenized cookie value
|
* @param cookieTokens the decoded and tokenized cookie value
|
||||||
* @param request the request
|
* @param request the request
|
||||||
* @param response the response, to allow the cookie to be modified if required.
|
* @param response the response, to allow the cookie to be modified if required.
|
||||||
* @return the name of the corresponding user account if the cookie was validated successfully.
|
* @return the UserDetails for the corresponding user account if the cookie was validated successfully.
|
||||||
* @throws RememberMeAuthenticationException if the cookie is invalid or the login is invalid for some
|
* @throws RememberMeAuthenticationException if the cookie is invalid or the login is invalid for some
|
||||||
* other reason.
|
* other reason.
|
||||||
|
* @throws UsernameNotFoundException if the user account corresponding to the login cookie couldn't be found
|
||||||
|
* (for example if the user has been removed from the system).
|
||||||
*/
|
*/
|
||||||
protected abstract String processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,
|
protected abstract UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,
|
||||||
HttpServletResponse response) throws RememberMeAuthenticationException;
|
HttpServletResponse response) throws RememberMeAuthenticationException, UsernameNotFoundException;
|
||||||
|
|
||||||
protected void cancelCookie(HttpServletRequest request, HttpServletResponse response) {
|
protected void cancelCookie(HttpServletRequest request, HttpServletResponse response) {
|
||||||
logger.debug("Cancelling cookie");
|
logger.debug("Cancelling cookie");
|
||||||
@ -315,6 +322,10 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
|
|||||||
this.key = key;
|
this.key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
public void setTokenValiditySeconds(int tokenValiditySeconds) {
|
public void setTokenValiditySeconds(int tokenValiditySeconds) {
|
||||||
this.tokenValiditySeconds = tokenValiditySeconds;
|
this.tokenValiditySeconds = tokenValiditySeconds;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package org.springframework.security.ui.rememberme;
|
package org.springframework.security.ui.rememberme;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Exception thrown by a RememberMeServices implementation to indicate
|
||||||
|
* that a submitted cookie is of an invalid format or has expired.
|
||||||
|
*
|
||||||
* @author Luke Taylor
|
* @author Luke Taylor
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
package org.springframework.security.ui.rememberme;
|
package org.springframework.security.ui.rememberme;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
import org.springframework.dao.DataAccessException;
|
|
||||||
import org.springframework.security.Authentication;
|
import org.springframework.security.Authentication;
|
||||||
|
import org.springframework.security.userdetails.UserDetails;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
@ -61,7 +63,7 @@ public class PersistentTokenBasedRememberMeServices extends AbstractRememberMeSe
|
|||||||
* @throws CookieTheftException if a presented series value is found, but the stored token is different from the
|
* @throws CookieTheftException if a presented series value is found, but the stored token is different from the
|
||||||
* one presented.
|
* one presented.
|
||||||
*/
|
*/
|
||||||
protected String processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) {
|
protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
|
||||||
if (cookieTokens.length != 2) {
|
if (cookieTokens.length != 2) {
|
||||||
throw new InvalidCookieException("Cookie token did not contain " + 2 +
|
throw new InvalidCookieException("Cookie token did not contain " + 2 +
|
||||||
@ -108,7 +110,11 @@ public class PersistentTokenBasedRememberMeServices extends AbstractRememberMeSe
|
|||||||
throw new RememberMeAuthenticationException("Autologin failed due to data access problem");
|
throw new RememberMeAuthenticationException("Autologin failed due to data access problem");
|
||||||
}
|
}
|
||||||
|
|
||||||
return token.getUsername();
|
UserDetails user = getUserDetailsService().loadUserByUsername(token.getUsername());
|
||||||
|
|
||||||
|
validateUserDetails(user);
|
||||||
|
|
||||||
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,28 +15,16 @@
|
|||||||
|
|
||||||
package org.springframework.security.ui.rememberme;
|
package org.springframework.security.ui.rememberme;
|
||||||
|
|
||||||
import java.util.Date;
|
import org.springframework.security.Authentication;
|
||||||
|
import org.springframework.security.userdetails.UserDetails;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
|
||||||
import javax.servlet.http.Cookie;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.util.Arrays;
|
||||||
import org.springframework.security.Authentication;
|
import java.util.Date;
|
||||||
import org.springframework.security.providers.rememberme.RememberMeAuthenticationToken;
|
|
||||||
import org.springframework.security.ui.AuthenticationDetailsSource;
|
|
||||||
import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
|
|
||||||
import org.springframework.security.ui.logout.LogoutHandler;
|
|
||||||
import org.springframework.security.userdetails.UserDetails;
|
|
||||||
import org.springframework.security.userdetails.UserDetailsService;
|
|
||||||
import org.springframework.security.userdetails.UsernameNotFoundException;
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
|
||||||
import org.apache.commons.codec.digest.DigestUtils;
|
|
||||||
import org.apache.commons.logging.Log;
|
|
||||||
import org.apache.commons.logging.LogFactory;
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.web.bind.RequestUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Identifies previously remembered users by a Base-64 encoded cookie.
|
* Identifies previously remembered users by a Base-64 encoded cookie.
|
||||||
@ -44,8 +32,7 @@ import org.springframework.web.bind.RequestUtils;
|
|||||||
* <p>
|
* <p>
|
||||||
* This implementation does not rely on an external database, so is attractive
|
* This implementation does not rely on an external database, so is attractive
|
||||||
* for simple applications. The cookie will be valid for a specific period from
|
* for simple applications. The cookie will be valid for a specific period from
|
||||||
* the date of the last
|
* the date of the last {@link #loginSuccess(HttpServletRequest, HttpServletResponse, Authentication)}.
|
||||||
* {@link #loginSuccess(HttpServletRequest, HttpServletResponse, Authentication)}.
|
|
||||||
* As per the interface contract, this method will only be called when the
|
* As per the interface contract, this method will only be called when the
|
||||||
* principal completes a successful interactive authentication. As such the time
|
* principal completes a successful interactive authentication. As such the time
|
||||||
* period commences from the last authentication attempt where they furnished
|
* period commences from the last authentication attempt where they furnished
|
||||||
@ -72,7 +59,7 @@ import org.springframework.web.bind.RequestUtils;
|
|||||||
*
|
*
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* As such, if the user changes their password any remember-me token will be
|
* As such, if the user changes their password, any remember-me token will be
|
||||||
* invalidated. Equally, the system administrator may invalidate every
|
* invalidated. Equally, the system administrator may invalidate every
|
||||||
* remember-me token on issue by changing the key. This provides some reasonable
|
* remember-me token on issue by changing the key. This provides some reasonable
|
||||||
* approaches to recovering from a remember-me token being left on a public
|
* approaches to recovering from a remember-me token being left on a public
|
||||||
@ -86,91 +73,27 @@ import org.springframework.web.bind.RequestUtils;
|
|||||||
* <p>
|
* <p>
|
||||||
* This is a basic remember-me implementation which is suitable for many
|
* This is a basic remember-me implementation which is suitable for many
|
||||||
* applications. However, we recommend a database-based implementation if you
|
* applications. However, we recommend a database-based implementation if you
|
||||||
* require a more secure remember-me approach.
|
* require a more secure remember-me approach (see {@link PersistentTokenBasedRememberMeServices}).
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* By default the tokens will be valid for 14 days from the last successful
|
* By default the tokens will be valid for 14 days from the last successful
|
||||||
* authentication attempt. This can be changed using
|
* authentication attempt. This can be changed using
|
||||||
* {@link #setTokenValiditySeconds(long)}.
|
* {@link #setTokenValiditySeconds(int)}.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Ben Alex
|
* @author Ben Alex
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class TokenBasedRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler {
|
public class TokenBasedRememberMeServices extends AbstractRememberMeServices {
|
||||||
//~ Static fields/initializers =====================================================================================
|
|
||||||
|
|
||||||
public static final String SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY = "SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE";
|
|
||||||
|
|
||||||
public static final String DEFAULT_PARAMETER = "_spring_security_remember_me";
|
|
||||||
|
|
||||||
protected static final Log logger = LogFactory.getLog(TokenBasedRememberMeServices.class);
|
|
||||||
|
|
||||||
//~ Instance fields ================================================================================================
|
|
||||||
|
|
||||||
protected AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
|
|
||||||
|
|
||||||
private String key;
|
|
||||||
|
|
||||||
private String parameter = DEFAULT_PARAMETER;
|
|
||||||
|
|
||||||
private UserDetailsService userDetailsService;
|
|
||||||
|
|
||||||
protected long tokenValiditySeconds = 1209600; // 14 days
|
|
||||||
|
|
||||||
private boolean alwaysRemember = false;
|
|
||||||
|
|
||||||
private String cookieName = SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY;
|
|
||||||
|
|
||||||
//~ Methods ========================================================================================================
|
//~ Methods ========================================================================================================
|
||||||
|
|
||||||
public void afterPropertiesSet() throws Exception {
|
public UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request,
|
||||||
Assert.hasLength(key);
|
HttpServletResponse response) {
|
||||||
Assert.hasLength(parameter);
|
|
||||||
Assert.hasLength(cookieName);
|
|
||||||
Assert.notNull(userDetailsService);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
|
|
||||||
Cookie[] cookies = request.getCookies();
|
|
||||||
|
|
||||||
if ((cookies == null) || (cookies.length == 0)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < cookies.length; i++) {
|
|
||||||
if (!cookieName.equals(cookies[i].getName())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have the spring security cookie
|
|
||||||
String cookieValue = cookies[i].getValue();
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Remember-me cookie detected");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = 0; j < cookieValue.length() % 4; j++) {
|
|
||||||
cookieValue = cookieValue + "=";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Base64.isArrayByteBase64(cookieValue.getBytes())) {
|
|
||||||
cancelCookie(request, response, "Cookie token was not Base64 encoded; value was '" + cookieValue + "'");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode token from Base64
|
|
||||||
// format of token is:
|
|
||||||
// username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
|
|
||||||
String cookieAsPlainText = new String(Base64.decodeBase64(cookieValue.getBytes()));
|
|
||||||
String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":");
|
|
||||||
|
|
||||||
if (cookieTokens.length != 3) {
|
if (cookieTokens.length != 3) {
|
||||||
cancelCookie(request, response, "Cookie token did not contain 3 tokens; decoded value was '"
|
throw new InvalidCookieException("Cookie token did not contain " + 2 +
|
||||||
+ cookieAsPlainText + "'");
|
" tokens, but contained '" + Arrays.asList(cookieTokens) + "'");
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long tokenExpiryTime;
|
long tokenExpiryTime;
|
||||||
@ -179,273 +102,94 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial
|
|||||||
tokenExpiryTime = new Long(cookieTokens[1]).longValue();
|
tokenExpiryTime = new Long(cookieTokens[1]).longValue();
|
||||||
}
|
}
|
||||||
catch (NumberFormatException nfe) {
|
catch (NumberFormatException nfe) {
|
||||||
cancelCookie(request, response,
|
throw new InvalidCookieException("Cookie token[1] did not contain a valid number (contained '" +
|
||||||
"Cookie token[1] did not contain a valid number (contained '" + cookieTokens[1] + "')");
|
cookieTokens[1] + "')");
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTokenExpired(tokenExpiryTime)) {
|
if (isTokenExpired(tokenExpiryTime)) {
|
||||||
cancelCookie(request, response, "Cookie token[1] has expired (expired on '"
|
throw new InvalidCookieException("Cookie token[1] has expired (expired on '"
|
||||||
+ new Date(tokenExpiryTime) + "'; current time is '" + new Date() + "')");
|
+ new Date(tokenExpiryTime) + "'; current time is '" + new Date() + "')");
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check the user exists
|
// Check the user exists.
|
||||||
// Defer lookup until after expiry time checked, to
|
// Defer lookup until after expiry time checked, to possibly avoid expensive database call.
|
||||||
// possibly avoid expensive lookup
|
|
||||||
UserDetails userDetails = loadUserDetails(request, response, cookieTokens);
|
|
||||||
|
|
||||||
if (userDetails == null) {
|
UserDetails userDetails = getUserDetailsService().loadUserByUsername(cookieTokens[0]);
|
||||||
cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
|
|
||||||
+ "' but was not found");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isValidUserDetails(request, response, userDetails, cookieTokens)) {
|
validateUserDetails(userDetails);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check signature of token matches remaining details
|
// Check signature of token matches remaining details.
|
||||||
// Must do this after user lookup, as we need the
|
// Must do this after user lookup, as we need the DAO-derived password.
|
||||||
// DAO-derived password
|
// If efficiency was a major issue, just add in a UserCache implementation,
|
||||||
// If efficiency was a major issue, just add in a
|
// but recall that this method is usually only called once per HttpSession - if the token is valid,
|
||||||
// UserCache implementation,
|
// it will cause SecurityContextHolder population, whilst if invalid, will cause the cookie to be cancelled.
|
||||||
// but recall this method is usually only called one per
|
String expectedTokenSignature = makeTokenSignature(tokenExpiryTime, userDetails.getUsername(),
|
||||||
// HttpSession
|
userDetails.getPassword());
|
||||||
// (as if the token is valid, it will cause
|
|
||||||
// SecurityContextHolder population, whilst
|
|
||||||
// if invalid, will cause the cookie to be cancelled)
|
|
||||||
String expectedTokenSignature = makeTokenSignature(tokenExpiryTime, userDetails);
|
|
||||||
|
|
||||||
if (!expectedTokenSignature.equals(cookieTokens[2])) {
|
if (!expectedTokenSignature.equals(cookieTokens[2])) {
|
||||||
cancelCookie(request, response, "Cookie token[2] contained signature '" + cookieTokens[2]
|
throw new InvalidCookieException("Cookie token[2] contained signature '" + cookieTokens[2]
|
||||||
+ "' but expected '" + expectedTokenSignature + "'");
|
+ "' but expected '" + expectedTokenSignature + "'");
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// By this stage we have a valid token
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Remember-me cookie accepted");
|
|
||||||
}
|
|
||||||
|
|
||||||
RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key, userDetails,
|
|
||||||
userDetails.getAuthorities());
|
|
||||||
auth.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
|
|
||||||
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param tokenExpiryTime
|
|
||||||
* @param userDetails
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected String makeTokenSignature(long tokenExpiryTime, UserDetails userDetails) {
|
|
||||||
String expectedTokenSignature = DigestUtils.md5Hex(userDetails.getUsername() + ":" + tokenExpiryTime + ":"
|
|
||||||
+ userDetails.getPassword() + ":" + this.key);
|
|
||||||
return expectedTokenSignature;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isValidUserDetails(HttpServletRequest request, HttpServletResponse response,
|
|
||||||
UserDetails userDetails, String[] cookieTokens) {
|
|
||||||
// Immediately reject if the user is not allowed to
|
|
||||||
// login
|
|
||||||
if (!userDetails.isAccountNonExpired() || !userDetails.isCredentialsNonExpired() || !userDetails.isEnabled()) {
|
|
||||||
cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
|
|
||||||
+ "' but account has expired, credentials have expired, or user is disabled");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected UserDetails loadUserDetails(HttpServletRequest request, HttpServletResponse response,
|
|
||||||
String[] cookieTokens) {
|
|
||||||
UserDetails userDetails = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
userDetails = this.userDetailsService.loadUserByUsername(cookieTokens[0]);
|
|
||||||
}
|
|
||||||
catch (UsernameNotFoundException notFound) {
|
|
||||||
cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
|
|
||||||
+ "' but was not found");
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return userDetails;
|
return userDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the digital signature to be put in the cookie. Default value is
|
||||||
|
* MD5 ("username:tokenExpiryTime:password:key")
|
||||||
|
*/
|
||||||
|
protected String makeTokenSignature(long tokenExpiryTime, String username, String password) {
|
||||||
|
return DigestUtils.md5Hex(username + ":" + tokenExpiryTime + ":" + password + ":" + getKey());
|
||||||
|
}
|
||||||
|
|
||||||
protected boolean isTokenExpired(long tokenExpiryTime) {
|
protected boolean isTokenExpired(long tokenExpiryTime) {
|
||||||
// Check it has not expired
|
return tokenExpiryTime < System.currentTimeMillis();
|
||||||
if (tokenExpiryTime < System.currentTimeMillis()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void cancelCookie(HttpServletRequest request, HttpServletResponse response, String reasonForLog) {
|
public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,
|
||||||
if ((reasonForLog != null) && logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Cancelling cookie for reason: " + reasonForLog);
|
|
||||||
}
|
|
||||||
|
|
||||||
response.addCookie(makeCancelCookie(request));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getParameter() {
|
|
||||||
return parameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getTokenValiditySeconds() {
|
|
||||||
return tokenValiditySeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UserDetailsService getUserDetailsService() {
|
|
||||||
return userDetailsService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loginFail(HttpServletRequest request, HttpServletResponse response) {
|
|
||||||
cancelCookie(request, response, "Interactive authentication attempt was unsuccessful");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
|
|
||||||
if (alwaysRemember) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return RequestUtils.getBooleanParameter(request, parameter, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loginSuccess(HttpServletRequest request, HttpServletResponse response,
|
|
||||||
Authentication successfulAuthentication) {
|
Authentication successfulAuthentication) {
|
||||||
// Exit if the principal hasn't asked to be remembered
|
|
||||||
if (!rememberMeRequested(request, parameter)) {
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Did not send remember-me cookie (principal did not set parameter '" + this.parameter
|
|
||||||
+ "')");
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine username and password, ensuring empty strings
|
|
||||||
Assert.notNull(successfulAuthentication.getPrincipal());
|
|
||||||
Assert.notNull(successfulAuthentication.getCredentials());
|
|
||||||
|
|
||||||
String username = retrieveUserName(successfulAuthentication);
|
String username = retrieveUserName(successfulAuthentication);
|
||||||
String password = retrievePassword(successfulAuthentication);
|
String password = retrievePassword(successfulAuthentication);
|
||||||
|
|
||||||
// If unable to find a username and password, just abort as
|
// If unable to find a username and password, just abort as TokenBasedRememberMeServices is
|
||||||
// TokenBasedRememberMeServices unable to construct a valid token in
|
// unable to construct a valid token in this case.
|
||||||
// this case
|
|
||||||
if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
|
if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long expiryTime = System.currentTimeMillis() + (tokenValiditySeconds * 1000);
|
long expiryTime = System.currentTimeMillis() + getTokenValiditySeconds() * 1000;
|
||||||
|
|
||||||
// construct token to put in cookie; format is:
|
String signatureValue = makeTokenSignature(expiryTime, username, password);
|
||||||
// username + ":" + expiryTime + ":" + Md5Hex(username + ":" +
|
String cookieValue = encodeCookie(new String[] {username, Long.toString(expiryTime), signatureValue});
|
||||||
// expiryTime + ":" + password + ":" + key)
|
|
||||||
String signatureValue = DigestUtils.md5Hex(username + ":" + expiryTime + ":" + password + ":" + key);
|
response.addCookie(makeValidCookie(cookieValue, request, getTokenValiditySeconds()));
|
||||||
String tokenValue = username + ":" + expiryTime + ":" + signatureValue;
|
|
||||||
String tokenValueBase64 = new String(Base64.encodeBase64(tokenValue.getBytes()));
|
|
||||||
response.addCookie(makeValidCookie(tokenValueBase64, request, tokenValiditySeconds));
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Added remember-me cookie for user '"
|
logger.debug("Added remember-me cookie for user '" + username + "', expiry: '"
|
||||||
+ username + "', expiry: '" + new Date(expiryTime) + "'");
|
+ new Date(expiryTime) + "'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
protected String retrieveUserName(Authentication authentication) {
|
||||||
cancelCookie(request, response, "Logout of user "
|
if (isInstanceOfUserDetails(authentication)) {
|
||||||
+ (authentication == null ? "Unknown" : authentication.getName()));
|
return ((UserDetails) authentication.getPrincipal()).getUsername();
|
||||||
}
|
|
||||||
|
|
||||||
protected String retrieveUserName(Authentication successfulAuthentication) {
|
|
||||||
if (isInstanceOfUserDetails(successfulAuthentication)) {
|
|
||||||
return ((UserDetails) successfulAuthentication.getPrincipal()).getUsername();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return successfulAuthentication.getPrincipal().toString();
|
return authentication.getPrincipal().toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String retrievePassword(Authentication successfulAuthentication) {
|
protected String retrievePassword(Authentication authentication) {
|
||||||
if (isInstanceOfUserDetails(successfulAuthentication)) {
|
if (isInstanceOfUserDetails(authentication)) {
|
||||||
return ((UserDetails) successfulAuthentication.getPrincipal()).getPassword();
|
return ((UserDetails) authentication.getPrincipal()).getPassword();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return successfulAuthentication.getCredentials().toString();
|
return authentication.getCredentials().toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isInstanceOfUserDetails(Authentication authentication) {
|
private boolean isInstanceOfUserDetails(Authentication authentication) {
|
||||||
return authentication.getPrincipal() instanceof UserDetails;
|
return authentication.getPrincipal() instanceof UserDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Cookie makeCancelCookie(HttpServletRequest request) {
|
|
||||||
Cookie cookie = new Cookie(cookieName, null);
|
|
||||||
cookie.setMaxAge(0);
|
|
||||||
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
|
|
||||||
|
|
||||||
return cookie;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Cookie makeValidCookie(String tokenValueBase64, HttpServletRequest request, long maxAge) {
|
|
||||||
Cookie cookie = new Cookie(cookieName, tokenValueBase64);
|
|
||||||
cookie.setMaxAge(new Long(maxAge).intValue());
|
|
||||||
cookie.setPath(StringUtils.hasLength(request.getContextPath()) ? request.getContextPath() : "/");
|
|
||||||
|
|
||||||
return cookie;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
|
|
||||||
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
|
|
||||||
this.authenticationDetailsSource = authenticationDetailsSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setKey(String key) {
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setParameter(String parameter) {
|
|
||||||
this.parameter = parameter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCookieName(String cookieName) {
|
|
||||||
this.cookieName = cookieName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTokenValiditySeconds(long tokenValiditySeconds) {
|
|
||||||
this.tokenValiditySeconds = tokenValiditySeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUserDetailsService(UserDetailsService userDetailsService) {
|
|
||||||
this.userDetailsService = userDetailsService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAlwaysRemember() {
|
|
||||||
return alwaysRemember;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAlwaysRemember(boolean alwaysRemember) {
|
|
||||||
this.alwaysRemember = alwaysRemember;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCookieName() {
|
|
||||||
return cookieName;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
public class AbstractRememberMeServicesTests {
|
public class AbstractRememberMeServicesTests {
|
||||||
User joe = new User("joe", "password", true, true,true,true, new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_A")});
|
static User joe = new User("joe", "password", true, true,true,true, new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_A")});
|
||||||
|
|
||||||
@Test(expected = InvalidCookieException.class)
|
@Test(expected = InvalidCookieException.class)
|
||||||
public void nonBase64CookieShouldBeDetected() {
|
public void nonBase64CookieShouldBeDetected() {
|
||||||
@ -57,20 +57,20 @@ public class AbstractRememberMeServicesTests {
|
|||||||
assertNull(services.autoLogin(request, response));
|
assertNull(services.autoLogin(request, response));
|
||||||
|
|
||||||
// shouldn't try to invalidate our cookie
|
// shouldn't try to invalidate our cookie
|
||||||
assertNull(response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_PERSISTENT_REMEMBER_ME_COOKIE_KEY));
|
assertNull(response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY));
|
||||||
|
|
||||||
request = new MockHttpServletRequest();
|
request = new MockHttpServletRequest();
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
// set non-login cookie
|
// set non-login cookie
|
||||||
request.setCookies(new Cookie[] {new Cookie("mycookie", "cookie")});
|
request.setCookies(new Cookie[] {new Cookie("mycookie", "cookie")});
|
||||||
assertNull(services.autoLogin(request, response));
|
assertNull(services.autoLogin(request, response));
|
||||||
assertNull(response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_PERSISTENT_REMEMBER_ME_COOKIE_KEY));
|
assertNull(response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void successfulAutoLoginReturnsExpectedAuthentication() {
|
public void successfulAutoLoginReturnsExpectedAuthentication() {
|
||||||
MockRememberMeServices services = new MockRememberMeServices();
|
MockRememberMeServices services = new MockRememberMeServices();
|
||||||
services.setUserDetailsService(new MockAuthenticationDao(joe, false));
|
services.setUserDetailsService(new MockUserDetailsService(joe, false));
|
||||||
assertNotNull(services.getUserDetailsService());
|
assertNotNull(services.getUserDetailsService());
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
@ -86,7 +86,7 @@ public class AbstractRememberMeServicesTests {
|
|||||||
@Test
|
@Test
|
||||||
public void autoLoginShouldFailIfInvalidCookieExceptionIsRaised() {
|
public void autoLoginShouldFailIfInvalidCookieExceptionIsRaised() {
|
||||||
MockRememberMeServices services = new MockRememberMeServices();
|
MockRememberMeServices services = new MockRememberMeServices();
|
||||||
services.setUserDetailsService(new MockAuthenticationDao(joe, true));
|
services.setUserDetailsService(new MockUserDetailsService(joe, true));
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
// Wrong number of tokes
|
// Wrong number of tokes
|
||||||
@ -103,7 +103,7 @@ public class AbstractRememberMeServicesTests {
|
|||||||
@Test
|
@Test
|
||||||
public void autoLoginShouldFailIfUserNotFound() {
|
public void autoLoginShouldFailIfUserNotFound() {
|
||||||
MockRememberMeServices services = new MockRememberMeServices();
|
MockRememberMeServices services = new MockRememberMeServices();
|
||||||
services.setUserDetailsService(new MockAuthenticationDao(joe, true));
|
services.setUserDetailsService(new MockUserDetailsService(joe, true));
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setCookies(createLoginCookie("cookie:1:2"));
|
request.setCookies(createLoginCookie("cookie:1:2"));
|
||||||
@ -120,7 +120,7 @@ public class AbstractRememberMeServicesTests {
|
|||||||
public void autoLoginShouldFailIfUserAccountIsLocked() {
|
public void autoLoginShouldFailIfUserAccountIsLocked() {
|
||||||
MockRememberMeServices services = new MockRememberMeServices();
|
MockRememberMeServices services = new MockRememberMeServices();
|
||||||
User joeLocked = new User("joe", "password",false,true,true,true,joe.getAuthorities());
|
User joeLocked = new User("joe", "password",false,true,true,true,joe.getAuthorities());
|
||||||
services.setUserDetailsService(new MockAuthenticationDao(joeLocked, false));
|
services.setUserDetailsService(new MockUserDetailsService(joeLocked, false));
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setCookies(createLoginCookie("cookie:1:2"));
|
request.setCookies(createLoginCookie("cookie:1:2"));
|
||||||
@ -136,7 +136,7 @@ public class AbstractRememberMeServicesTests {
|
|||||||
@Test
|
@Test
|
||||||
public void loginFailShouldCancelCookie() {
|
public void loginFailShouldCancelCookie() {
|
||||||
MockRememberMeServices services = new MockRememberMeServices();
|
MockRememberMeServices services = new MockRememberMeServices();
|
||||||
services.setUserDetailsService(new MockAuthenticationDao(joe, true));
|
services.setUserDetailsService(new MockUserDetailsService(joe, true));
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setContextPath("contextpath");
|
request.setContextPath("contextpath");
|
||||||
@ -151,12 +151,12 @@ public class AbstractRememberMeServicesTests {
|
|||||||
@Test(expected = CookieTheftException.class)
|
@Test(expected = CookieTheftException.class)
|
||||||
public void cookieTheftExceptionShouldBeRethrown() {
|
public void cookieTheftExceptionShouldBeRethrown() {
|
||||||
MockRememberMeServices services = new MockRememberMeServices() {
|
MockRememberMeServices services = new MockRememberMeServices() {
|
||||||
protected String processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) {
|
protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) {
|
||||||
throw new CookieTheftException("Pretending cookie was stolen");
|
throw new CookieTheftException("Pretending cookie was stolen");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
services.setUserDetailsService(new MockAuthenticationDao(joe, false));
|
services.setUserDetailsService(new MockUserDetailsService(joe, false));
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
|
|
||||||
request.setCookies(createLoginCookie("cookie:1:2"));
|
request.setCookies(createLoginCookie("cookie:1:2"));
|
||||||
@ -203,7 +203,6 @@ public class AbstractRememberMeServicesTests {
|
|||||||
services.setAlwaysRemember(true);
|
services.setAlwaysRemember(true);
|
||||||
services.loginSuccess(request, response, auth);
|
services.loginSuccess(request, response, auth);
|
||||||
assertTrue(services.loginSuccessCalled);
|
assertTrue(services.loginSuccessCalled);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -222,14 +221,14 @@ public class AbstractRememberMeServicesTests {
|
|||||||
|
|
||||||
private Cookie[] createLoginCookie(String cookieToken) {
|
private Cookie[] createLoginCookie(String cookieToken) {
|
||||||
MockRememberMeServices services = new MockRememberMeServices();
|
MockRememberMeServices services = new MockRememberMeServices();
|
||||||
Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_PERSISTENT_REMEMBER_ME_COOKIE_KEY,
|
Cookie cookie = new Cookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
||||||
services.encodeCookie(StringUtils.delimitedListToStringArray(cookieToken, ":")));
|
services.encodeCookie(StringUtils.delimitedListToStringArray(cookieToken, ":")));
|
||||||
|
|
||||||
return new Cookie[] {cookie};
|
return new Cookie[] {cookie};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertCookieCancelled(MockHttpServletResponse response) {
|
private void assertCookieCancelled(MockHttpServletResponse response) {
|
||||||
Cookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_PERSISTENT_REMEMBER_ME_COOKIE_KEY);
|
Cookie returnedCookie = response.getCookie(AbstractRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
||||||
assertNotNull(returnedCookie);
|
assertNotNull(returnedCookie);
|
||||||
assertEquals(0, returnedCookie.getMaxAge());
|
assertEquals(0, returnedCookie.getMaxAge());
|
||||||
}
|
}
|
||||||
@ -247,20 +246,23 @@ public class AbstractRememberMeServicesTests {
|
|||||||
loginSuccessCalled = true;
|
loginSuccessCalled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) throws RememberMeAuthenticationException {
|
protected UserDetails processAutoLoginCookie(String[] cookieTokens, HttpServletRequest request, HttpServletResponse response) throws RememberMeAuthenticationException {
|
||||||
if(cookieTokens.length != 3) {
|
if(cookieTokens.length != 3) {
|
||||||
throw new InvalidCookieException("deliberate exception");
|
throw new InvalidCookieException("deliberate exception");
|
||||||
}
|
}
|
||||||
|
|
||||||
return "joe";
|
UserDetails user = getUserDetailsService().loadUserByUsername("joe");
|
||||||
|
validateUserDetails(user);
|
||||||
|
|
||||||
|
return user;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MockAuthenticationDao implements UserDetailsService {
|
public static class MockUserDetailsService implements UserDetailsService {
|
||||||
private UserDetails toReturn;
|
private UserDetails toReturn;
|
||||||
private boolean throwException;
|
private boolean throwException;
|
||||||
|
|
||||||
public MockAuthenticationDao(UserDetails toReturn, boolean throwException) {
|
public MockUserDetailsService(UserDetails toReturn, boolean throwException) {
|
||||||
this.toReturn = toReturn;
|
this.toReturn = toReturn;
|
||||||
this.throwException = throwException;
|
this.throwException = throwException;
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package org.springframework.security.ui.rememberme;
|
package org.springframework.security.ui.rememberme;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.MockHttpServletResponse;
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
@ -20,6 +22,8 @@ public class PersistentTokenBasedRememberMeServicesTests {
|
|||||||
public void setUpData() throws Exception {
|
public void setUpData() throws Exception {
|
||||||
services = new PersistentTokenBasedRememberMeServices();
|
services = new PersistentTokenBasedRememberMeServices();
|
||||||
services.setCookieName("mycookiename");
|
services.setCookieName("mycookiename");
|
||||||
|
services.setUserDetailsService(
|
||||||
|
new AbstractRememberMeServicesTests.MockUserDetailsService(AbstractRememberMeServicesTests.joe, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = InvalidCookieException.class)
|
@Test(expected = InvalidCookieException.class)
|
||||||
|
@ -53,7 +53,6 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
//~ Constructors ===================================================================================================
|
//~ Constructors ===================================================================================================
|
||||||
|
|
||||||
public TokenBasedRememberMeServicesTests() {
|
public TokenBasedRememberMeServicesTests() {
|
||||||
super();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TokenBasedRememberMeServicesTests(String arg0) {
|
public TokenBasedRememberMeServicesTests(String arg0) {
|
||||||
@ -85,10 +84,6 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
return tokenValueBase64;
|
return tokenValueBase64;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
junit.textui.TestRunner.run(TokenBasedRememberMeServicesTests.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAutoLoginIfDoesNotPresentAnyCookies()
|
public void testAutoLoginIfDoesNotPresentAnyCookies()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
|
TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
|
||||||
@ -105,7 +100,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
|
|
||||||
assertNull(result);
|
assertNull(result);
|
||||||
|
|
||||||
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY);
|
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
||||||
assertNull(returnedCookie); // shouldn't try to invalidate our cookie
|
assertNull(returnedCookie); // shouldn't try to invalidate our cookie
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,7 +121,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
|
|
||||||
assertNull(result);
|
assertNull(result);
|
||||||
|
|
||||||
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY);
|
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
||||||
assertNull(returnedCookie); // shouldn't try to invalidate our cookie
|
assertNull(returnedCookie); // shouldn't try to invalidate our cookie
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,7 +134,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
services.setUserDetailsService(new MockAuthenticationDao(user, false));
|
services.setUserDetailsService(new MockAuthenticationDao(user, false));
|
||||||
// services.afterPropertiesSet();
|
// services.afterPropertiesSet();
|
||||||
|
|
||||||
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
|
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
||||||
generateCorrectCookieContentForToken(System.currentTimeMillis() - 1000000, "someone", "password", "key"));
|
generateCorrectCookieContentForToken(System.currentTimeMillis() - 1000000, "someone", "password", "key"));
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setCookies(new Cookie[] {cookie});
|
request.setCookies(new Cookie[] {cookie});
|
||||||
@ -150,7 +145,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
|
|
||||||
assertNull(result);
|
assertNull(result);
|
||||||
|
|
||||||
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY);
|
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
||||||
assertNotNull(returnedCookie);
|
assertNotNull(returnedCookie);
|
||||||
assertEquals(0, returnedCookie.getMaxAge());
|
assertEquals(0, returnedCookie.getMaxAge());
|
||||||
}
|
}
|
||||||
@ -165,7 +160,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
services.setUserDetailsService(new MockAuthenticationDao(user, false));
|
services.setUserDetailsService(new MockAuthenticationDao(user, false));
|
||||||
//services.afterPropertiesSet();
|
//services.afterPropertiesSet();
|
||||||
|
|
||||||
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
|
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
||||||
new String(Base64.encodeBase64("x".getBytes())));
|
new String(Base64.encodeBase64("x".getBytes())));
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setCookies(new Cookie[] {cookie});
|
request.setCookies(new Cookie[] {cookie});
|
||||||
@ -176,7 +171,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
|
|
||||||
assertNull(result);
|
assertNull(result);
|
||||||
|
|
||||||
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY);
|
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
||||||
assertNotNull(returnedCookie);
|
assertNotNull(returnedCookie);
|
||||||
assertEquals(0, returnedCookie.getMaxAge());
|
assertEquals(0, returnedCookie.getMaxAge());
|
||||||
}
|
}
|
||||||
@ -190,7 +185,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
services.setUserDetailsService(new MockAuthenticationDao(user, false));
|
services.setUserDetailsService(new MockAuthenticationDao(user, false));
|
||||||
//services.afterPropertiesSet();
|
//services.afterPropertiesSet();
|
||||||
|
|
||||||
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
|
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
||||||
"NOT_BASE_64_ENCODED");
|
"NOT_BASE_64_ENCODED");
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setCookies(new Cookie[] {cookie});
|
request.setCookies(new Cookie[] {cookie});
|
||||||
@ -201,7 +196,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
|
|
||||||
assertNull(result);
|
assertNull(result);
|
||||||
|
|
||||||
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY);
|
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
||||||
assertNotNull(returnedCookie);
|
assertNotNull(returnedCookie);
|
||||||
assertEquals(0, returnedCookie.getMaxAge());
|
assertEquals(0, returnedCookie.getMaxAge());
|
||||||
}
|
}
|
||||||
@ -216,7 +211,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
services.setUserDetailsService(new MockAuthenticationDao(user, false));
|
services.setUserDetailsService(new MockAuthenticationDao(user, false));
|
||||||
//services.afterPropertiesSet();
|
//services.afterPropertiesSet();
|
||||||
|
|
||||||
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
|
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
||||||
generateCorrectCookieContentForToken(System.currentTimeMillis() + 1000000, "someone", "password",
|
generateCorrectCookieContentForToken(System.currentTimeMillis() + 1000000, "someone", "password",
|
||||||
"WRONG_KEY"));
|
"WRONG_KEY"));
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
@ -228,7 +223,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
|
|
||||||
assertNull(result);
|
assertNull(result);
|
||||||
|
|
||||||
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY);
|
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
||||||
assertNotNull(returnedCookie);
|
assertNotNull(returnedCookie);
|
||||||
assertEquals(0, returnedCookie.getMaxAge());
|
assertEquals(0, returnedCookie.getMaxAge());
|
||||||
}
|
}
|
||||||
@ -243,7 +238,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
services.setUserDetailsService(new MockAuthenticationDao(user, false));
|
services.setUserDetailsService(new MockAuthenticationDao(user, false));
|
||||||
//services.afterPropertiesSet();
|
//services.afterPropertiesSet();
|
||||||
|
|
||||||
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
|
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
||||||
new String(Base64.encodeBase64("username:NOT_A_NUMBER:signature".getBytes())));
|
new String(Base64.encodeBase64("username:NOT_A_NUMBER:signature".getBytes())));
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setCookies(new Cookie[] {cookie});
|
request.setCookies(new Cookie[] {cookie});
|
||||||
@ -254,7 +249,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
|
|
||||||
assertNull(result);
|
assertNull(result);
|
||||||
|
|
||||||
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY);
|
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
||||||
assertNotNull(returnedCookie);
|
assertNotNull(returnedCookie);
|
||||||
assertEquals(0, returnedCookie.getMaxAge());
|
assertEquals(0, returnedCookie.getMaxAge());
|
||||||
}
|
}
|
||||||
@ -265,7 +260,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
services.setUserDetailsService(new MockAuthenticationDao(null, true));
|
services.setUserDetailsService(new MockAuthenticationDao(null, true));
|
||||||
//services.afterPropertiesSet();
|
//services.afterPropertiesSet();
|
||||||
|
|
||||||
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
|
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
||||||
generateCorrectCookieContentForToken(System.currentTimeMillis() + 1000000, "someone", "password", "key"));
|
generateCorrectCookieContentForToken(System.currentTimeMillis() + 1000000, "someone", "password", "key"));
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setCookies(new Cookie[] {cookie});
|
request.setCookies(new Cookie[] {cookie});
|
||||||
@ -276,7 +271,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
|
|
||||||
assertNull(result);
|
assertNull(result);
|
||||||
|
|
||||||
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY);
|
Cookie returnedCookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
||||||
assertNotNull(returnedCookie);
|
assertNotNull(returnedCookie);
|
||||||
assertEquals(0, returnedCookie.getMaxAge());
|
assertEquals(0, returnedCookie.getMaxAge());
|
||||||
}
|
}
|
||||||
@ -290,7 +285,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
services.setUserDetailsService(new MockAuthenticationDao(user, false));
|
services.setUserDetailsService(new MockAuthenticationDao(user, false));
|
||||||
// services.afterPropertiesSet();
|
// services.afterPropertiesSet();
|
||||||
|
|
||||||
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY,
|
Cookie cookie = new Cookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY,
|
||||||
generateCorrectCookieContentForToken(System.currentTimeMillis() + 1000000, "someone", "password", "key"));
|
generateCorrectCookieContentForToken(System.currentTimeMillis() + 1000000, "someone", "password", "key"));
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
request.setCookies(new Cookie[] {cookie});
|
request.setCookies(new Cookie[] {cookie});
|
||||||
@ -330,7 +325,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
services.loginFail(request, response);
|
services.loginFail(request, response);
|
||||||
|
|
||||||
Cookie cookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY);
|
Cookie cookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
||||||
assertNotNull(cookie);
|
assertNotNull(cookie);
|
||||||
assertEquals(0, cookie.getMaxAge());
|
assertEquals(0, cookie.getMaxAge());
|
||||||
}
|
}
|
||||||
@ -346,7 +341,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
new TestingAuthenticationToken("someone", "password",
|
new TestingAuthenticationToken("someone", "password",
|
||||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ABC")}));
|
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ABC")}));
|
||||||
|
|
||||||
Cookie cookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY);
|
Cookie cookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
||||||
assertNull(cookie);
|
assertNull(cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,7 +356,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
new TestingAuthenticationToken("someone", "password",
|
new TestingAuthenticationToken("someone", "password",
|
||||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ABC")}));
|
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ABC")}));
|
||||||
|
|
||||||
Cookie cookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY);
|
Cookie cookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
||||||
assertNotNull(cookie);
|
assertNotNull(cookie);
|
||||||
assertEquals(services.getTokenValiditySeconds(), cookie.getMaxAge());
|
assertEquals(services.getTokenValiditySeconds(), cookie.getMaxAge());
|
||||||
assertTrue(Base64.isArrayByteBase64(cookie.getValue().getBytes()));
|
assertTrue(Base64.isArrayByteBase64(cookie.getValue().getBytes()));
|
||||||
@ -381,7 +376,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||||||
new TestingAuthenticationToken(user, "ignored",
|
new TestingAuthenticationToken(user, "ignored",
|
||||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ABC")}));
|
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ABC")}));
|
||||||
|
|
||||||
Cookie cookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY);
|
Cookie cookie = response.getCookie(TokenBasedRememberMeServices.SPRING_SECURITY_REMEMBER_ME_COOKIE_KEY);
|
||||||
assertNotNull(cookie);
|
assertNotNull(cookie);
|
||||||
assertEquals(services.getTokenValiditySeconds(), cookie.getMaxAge());
|
assertEquals(services.getTokenValiditySeconds(), cookie.getMaxAge());
|
||||||
assertTrue(Base64.isArrayByteBase64(cookie.getValue().getBytes()));
|
assertTrue(Base64.isArrayByteBase64(cookie.getValue().getBytes()));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user