Refactored autoLogin method to reduce nesting of conditionals and loops.

This commit is contained in:
Luke Taylor 2007-10-30 21:09:47 +00:00
parent 38210d2039
commit 8b199d38ed

View File

@ -16,7 +16,6 @@
package org.springframework.security.ui.rememberme; package org.springframework.security.ui.rememberme;
import java.util.Date; import java.util.Date;
import java.util.Map;
import javax.servlet.http.Cookie; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -24,7 +23,6 @@ import javax.servlet.http.HttpServletResponse;
import org.springframework.security.Authentication; import org.springframework.security.Authentication;
import org.springframework.security.providers.rememberme.RememberMeAuthenticationToken; import org.springframework.security.providers.rememberme.RememberMeAuthenticationToken;
import org.springframework.security.ui.AccessDeniedHandler;
import org.springframework.security.ui.AuthenticationDetailsSource; import org.springframework.security.ui.AuthenticationDetailsSource;
import org.springframework.security.ui.AuthenticationDetailsSourceImpl; import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
import org.springframework.security.ui.logout.LogoutHandler; import org.springframework.security.ui.logout.LogoutHandler;
@ -36,7 +34,6 @@ import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.bind.RequestUtils; import org.springframework.web.bind.RequestUtils;
@ -143,108 +140,105 @@ public class TokenBasedRememberMeServices implements RememberMeServices, Initial
} }
for (int i = 0; i < cookies.length; i++) { for (int i = 0; i < cookies.length; i++) {
if (cookieName.equals(cookies[i].getName())) { if (!cookieName.equals(cookies[i].getName())) {
String cookieValue = cookies[i].getValue(); continue;
}
for (int j = 0; j < cookieValue.length() % 4; j++) { // We have the spring security cookie
cookieValue = cookieValue + "="; String cookieValue = cookies[i].getValue();
}
if (Base64.isArrayByteBase64(cookieValue.getBytes())) { if (logger.isDebugEnabled()) {
if (logger.isDebugEnabled()) { logger.debug("Remember-me cookie detected");
logger.debug("Remember-me cookie detected"); }
}
// Decode token from Base64 for (int j = 0; j < cookieValue.length() % 4; j++) {
// format of token is: cookieValue = cookieValue + "=";
// 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 (!Base64.isArrayByteBase64(cookieValue.getBytes())) {
cancelCookie(request, response, "Cookie token was not Base64 encoded; value was '" + cookieValue + "'");
long tokenExpiryTime; return null;
}
try { // Decode token from Base64
tokenExpiryTime = new Long(cookieTokens[1]).longValue(); // format of token is:
} // username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
catch (NumberFormatException nfe) { String cookieAsPlainText = new String(Base64.decodeBase64(cookieValue.getBytes()));
cancelCookie(request, response, String[] cookieTokens = StringUtils.delimitedListToStringArray(cookieAsPlainText, ":");
"Cookie token[1] did not contain a valid number (contained '" + cookieTokens[1]
+ "')");
return null; if (cookieTokens.length != 3) {
} cancelCookie(request, response, "Cookie token did not contain 3 tokens; decoded value was '"
+ cookieAsPlainText + "'");
if (isTokenExpired(tokenExpiryTime)) { return null;
cancelCookie(request, response, "Cookie token[1] has expired (expired on '" }
+ new Date(tokenExpiryTime) + "'; current time is '" + new Date() + "')");
return null; long tokenExpiryTime;
}
// Check the user exists try {
// Defer lookup until after expiry time checked, to tokenExpiryTime = new Long(cookieTokens[1]).longValue();
// possibly avoid expensive lookup }
UserDetails userDetails = loadUserDetails(request, response, cookieTokens); catch (NumberFormatException nfe) {
cancelCookie(request, response,
"Cookie token[1] did not contain a valid number (contained '" + cookieTokens[1] + "')");
if (userDetails == null) { return null;
cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0] }
+ "' but was not found");
return null;
}
if (!isValidUserDetails(request, response, userDetails, cookieTokens)) { if (isTokenExpired(tokenExpiryTime)) {
return null; cancelCookie(request, response, "Cookie token[1] has expired (expired on '"
} + new Date(tokenExpiryTime) + "'; current time is '" + new Date() + "')");
// Check signature of token matches remaining details return null;
// Must do this after user lookup, as we need the }
// DAO-derived password
// If efficiency was a major issue, just add in a
// UserCache implementation,
// but recall this method is usually only called one per
// HttpSession
// (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])) { // Check the user exists
cancelCookie(request, response, "Cookie token[2] contained signature '" + cookieTokens[2] // Defer lookup until after expiry time checked, to
+ "' but expected '" + expectedTokenSignature + "'"); // possibly avoid expensive lookup
UserDetails userDetails = loadUserDetails(request, response, cookieTokens);
return null; if (userDetails == null) {
} cancelCookie(request, response, "Cookie token[0] contained username '" + cookieTokens[0]
+ "' but was not found");
return null;
}
// By this stage we have a valid token if (!isValidUserDetails(request, response, userDetails, cookieTokens)) {
if (logger.isDebugEnabled()) { return null;
logger.debug("Remember-me cookie accepted"); }
}
RememberMeAuthenticationToken auth = new RememberMeAuthenticationToken(this.key, userDetails, // Check signature of token matches remaining details
userDetails.getAuthorities()); // Must do this after user lookup, as we need the
auth.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request)); // DAO-derived password
// If efficiency was a major issue, just add in a
// UserCache implementation,
// but recall this method is usually only called one per
// HttpSession
// (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);
return auth; if (!expectedTokenSignature.equals(cookieTokens[2])) {
} cancelCookie(request, response, "Cookie token[2] contained signature '" + cookieTokens[2]
else { + "' but expected '" + expectedTokenSignature + "'");
cancelCookie(request, response, "Cookie token did not contain 3 tokens; decoded value was '"
+ cookieAsPlainText + "'");
return null; return null;
} }
}
else { // By this stage we have a valid token
cancelCookie(request, response, "Cookie token was not Base64 encoded; value was '" + cookieValue 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;
}
}
}
return null; return null;
} }