From ef38844a6d9ab9b8c2a29e0a9bb418b0d0105966 Mon Sep 17 00:00:00 2001 From: Vishal Puri Date: Wed, 27 Jun 2007 08:33:37 +0000 Subject: [PATCH] Improved comments and made TokenBasedRememberMeServices modular to support subclasses --- .../ui/AbstractProcessingFilter.java | 8 +- .../TokenBasedRememberMeServices.java | 134 ++++++++++++------ 2 files changed, 98 insertions(+), 44 deletions(-) diff --git a/core/src/main/java/org/acegisecurity/ui/AbstractProcessingFilter.java b/core/src/main/java/org/acegisecurity/ui/AbstractProcessingFilter.java index 8b68af9a28..934692afe5 100644 --- a/core/src/main/java/org/acegisecurity/ui/AbstractProcessingFilter.java +++ b/core/src/main/java/org/acegisecurity/ui/AbstractProcessingFilter.java @@ -127,7 +127,8 @@ import javax.servlet.http.HttpServletResponse; *

* * @author Ben Alex - * @version $Id$ + * @version $Id: AbstractProcessingFilter.java 1909 2007-06-19 04:08:19Z + * vishalpuri $ */ public abstract class AbstractProcessingFilter implements Filter, InitializingBean, ApplicationEventPublisherAware, MessageSourceAware, ApplicationContextAware { @@ -224,12 +225,15 @@ public abstract class AbstractProcessingFilter implements Filter, InitializingBe Assert.notNull(this.rememberMeServices); } + /** + * Use the first autodetected instance of RememberMeServices + */ private void autoDetectRememberMeServices() { if (applicationContext != null) { Map map = applicationContext.getBeansOfType(RememberMeServices.class); if (map.size() > 0) { setRememberMeServices((RememberMeServices) map.values().iterator().next()); - } + } } } diff --git a/core/src/main/java/org/acegisecurity/ui/rememberme/TokenBasedRememberMeServices.java b/core/src/main/java/org/acegisecurity/ui/rememberme/TokenBasedRememberMeServices.java index c1a684bc15..19ae8590ce 100644 --- a/core/src/main/java/org/acegisecurity/ui/rememberme/TokenBasedRememberMeServices.java +++ b/core/src/main/java/org/acegisecurity/ui/rememberme/TokenBasedRememberMeServices.java @@ -105,12 +105,11 @@ import org.springframework.web.bind.RequestUtils; * @version $Id: TokenBasedRememberMeServices.java 1871 2007-05-25 03:12:49Z * benalex $ */ -public class TokenBasedRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler, Ordered, ApplicationContextAware { +public class TokenBasedRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler, Ordered, + ApplicationContextAware { // ~ Static fields/initializers // ===================================================================================== - - public static final String ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE_KEY = "ACEGI_SECURITY_HASHED_REMEMBER_ME_COOKIE"; public static final String DEFAULT_PARAMETER = "_acegi_security_remember_me"; @@ -149,13 +148,14 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial Assert.hasLength(key); Assert.hasLength(parameter); Assert.hasLength(cookieName); - Assert.notNull(applicationContext, "ApplicationContext required"); - if (!isSetUserDetailsServiceInvoked) { - autoDetectAnyUserDetailsServiceAndUseIt(applicationContext); + if (applicationContext != null) { + if (!isSetUserDetailsServiceInvoked) { + autoDetectAndUseAnyUserDetailsService(applicationContext); + } } Assert.notNull(userDetailsService); } - + /** * Introspects the Applicationcontext for the single instance * of {@link AccessDeniedHandler}. If found invoke @@ -166,7 +166,7 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial * * @param applicationContext to locate the instance */ - private void autoDetectAnyUserDetailsServiceAndUseIt(ApplicationContext applicationContext) { + private void autoDetectAndUseAnyUserDetailsService(ApplicationContext applicationContext) { Map map = applicationContext.getBeansOfType(UserDetailsService.class); if (map.size() > 1) { throw new IllegalArgumentException( @@ -177,7 +177,6 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial setUserDetailsService((UserDetailsService) map.values().iterator().next()); } } - public Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) { Cookie[] cookies = request.getCookies(); @@ -208,6 +207,7 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":"); if (cookieTokens.length == 3) { + long tokenExpiryTime; try { @@ -221,8 +221,7 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial return null; } - // Check it has not expired - if (tokenExpiryTime < System.currentTimeMillis()) { + if (isTokenExpired(tokenExpiryTime)) { cancelCookie(request, response, "Cookie token[1] has expired (expired on '" + new Date(tokenExpiryTime) + "'; current time is '" + new Date() + "')"); @@ -232,25 +231,15 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial // Check the user exists // Defer lookup until after expiry time checked, to // possibly avoid expensive lookup - UserDetails userDetails; + UserDetails userDetails = loadUserDetails(request, response, cookieTokens); - try { - userDetails = this.userDetailsService.loadUserByUsername(cookieTokens[0]); - } - catch (UsernameNotFoundException notFound) { + if (userDetails == null) { cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0] + "' but was not found"); - return null; } - // 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"); - + if (!isValidUserDetails(request, response, userDetails, cookieTokens)) { return null; } @@ -264,8 +253,7 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial // (as if the token is valid, it will cause // SecurityContextHolder population, whilst // if invalid, will cause the cookie to be cancelled) - String expectedTokenSignature = DigestUtils.md5Hex(userDetails.getUsername() + ":" - + tokenExpiryTime + ":" + userDetails.getPassword() + ":" + this.key); + String expectedTokenSignature = makeTokenSignature(tokenExpiryTime, userDetails); if (!expectedTokenSignature.equals(cookieTokens[2])) { cancelCookie(request, response, "Cookie token[2] contained signature '" + cookieTokens[2] @@ -304,7 +292,55 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial return null; } - private void cancelCookie(HttpServletRequest request, HttpServletResponse response, String reasonForLog) { + /** + * @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; + } + + protected boolean isTokenExpired(long tokenExpiryTime) { + // Check it has not expired + if (tokenExpiryTime < System.currentTimeMillis()) { + return true; + } + return false; + } + + protected void cancelCookie(HttpServletRequest request, HttpServletResponse response, String reasonForLog) { if ((reasonForLog != null) && logger.isDebugEnabled()) { logger.debug("Cancelling cookie for reason: " + reasonForLog); } @@ -356,17 +392,8 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial Assert.notNull(successfulAuthentication.getPrincipal()); Assert.notNull(successfulAuthentication.getCredentials()); - String username; - String password; - - if (successfulAuthentication.getPrincipal() instanceof UserDetails) { - username = ((UserDetails) successfulAuthentication.getPrincipal()).getUsername(); - password = ((UserDetails) successfulAuthentication.getPrincipal()).getPassword(); - } - else { - username = successfulAuthentication.getPrincipal().toString(); - password = successfulAuthentication.getCredentials().toString(); - } + String username = retrieveUserName(successfulAuthentication); + String password = retrievePassword(successfulAuthentication); // If unable to find a username and password, just abort as // TokenBasedRememberMeServices unable to construct a valid token in @@ -375,9 +402,6 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial return; } - Assert.hasLength(username); - Assert.hasLength(password); - long expiryTime = System.currentTimeMillis() + (tokenValiditySeconds * 1000); // construct token to put in cookie; format is: @@ -400,6 +424,28 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial + (authentication == null ? "Unknown" : authentication.getName())); } + protected String retrieveUserName(Authentication successfulAuthentication) { + if (isInstanceOfUserDetails(successfulAuthentication)) { + return ((UserDetails) successfulAuthentication.getPrincipal()).getUsername(); + } + else { + return successfulAuthentication.getPrincipal().toString(); + } + } + + protected String retrievePassword(Authentication successfulAuthentication) { + if (isInstanceOfUserDetails(successfulAuthentication)) { + return ((UserDetails) successfulAuthentication.getPrincipal()).getPassword(); + } + else { + return successfulAuthentication.getCredentials().toString(); + } + } + + private boolean isInstanceOfUserDetails(Authentication authentication) { + return authentication.getPrincipal() instanceof UserDetails; + } + protected Cookie makeCancelCookie(HttpServletRequest request) { Cookie cookie = new Cookie(cookieName, null); cookie.setMaxAge(0); @@ -459,7 +505,11 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext=applicationContext; + this.applicationContext = applicationContext; + } + + public String getCookieName() { + return cookieName; } }