SEC-664: Removed validateUserDetails method from AbstractRememberMeServices, wrapped the UserDetailsService in a status-checking one and added a catch block for AccountStatusExceptions. Also some minor tidying up of other remember-me classes.
This commit is contained in:
parent
d3f26f09b6
commit
84c7ac5e57
|
@ -7,6 +7,7 @@ import org.springframework.beans.factory.InitializingBean;
|
|||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.security.Authentication;
|
||||
import org.springframework.security.SpringSecurityMessageSource;
|
||||
import org.springframework.security.AccountStatusException;
|
||||
import org.springframework.security.providers.rememberme.RememberMeAuthenticationToken;
|
||||
import org.springframework.security.ui.AuthenticationDetailsSource;
|
||||
import org.springframework.security.ui.AuthenticationDetailsSourceImpl;
|
||||
|
@ -14,6 +15,7 @@ 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.springframework.security.userdetails.decorator.StatusCheckingUserDetailsService;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
|
@ -27,6 +29,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class AbstractRememberMeServices implements RememberMeServices, InitializingBean, LogoutHandler {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
@ -91,6 +94,10 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
|
|||
cancelCookie(request, response);
|
||||
logger.debug("Invalid remember-me cookie: " + invalidCookie.getMessage());
|
||||
return null;
|
||||
} catch (AccountStatusException statusInvalid) {
|
||||
cancelCookie(request, response);
|
||||
logger.debug("Invalid UserDetails: " + statusInvalid.getMessage());
|
||||
return null;
|
||||
} catch (RememberMeAuthenticationException e) {
|
||||
cancelCookie(request, response);
|
||||
logger.debug(e.getMessage());
|
||||
|
@ -175,21 +182,6 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
if (!user.isAccountNonExpired() || !user.isCredentialsNonExpired() || !user.isEnabled()) {
|
||||
throw new RememberMeAuthenticationException("Remember-me login was valid for user " +
|
||||
user.getUsername() + ", but account is expired, has expired credentials or is disabled");
|
||||
}
|
||||
}
|
||||
|
||||
public final void loginFail(HttpServletRequest request, HttpServletResponse response) {
|
||||
logger.debug("Interactive login attempt was unsuccessful.");
|
||||
cancelCookie(request, response);
|
||||
|
@ -327,7 +319,7 @@ public abstract class AbstractRememberMeServices implements RememberMeServices,
|
|||
}
|
||||
|
||||
public void setUserDetailsService(UserDetailsService userDetailsService) {
|
||||
this.userDetailsService = userDetailsService;
|
||||
this.userDetailsService = new StatusCheckingUserDetailsService(userDetailsService);
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.util.Date;
|
|||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.0
|
||||
*/
|
||||
public class JdbcTokenRepositoryImpl extends JdbcDaoSupport implements PersistentTokenRepository {
|
||||
//~ Static fields/initializers =====================================================================================
|
||||
|
|
|
@ -34,6 +34,7 @@ import java.util.Date;
|
|||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.0
|
||||
*/
|
||||
public class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices {
|
||||
|
||||
|
@ -112,8 +113,6 @@ public class PersistentTokenBasedRememberMeServices extends AbstractRememberMeSe
|
|||
|
||||
UserDetails user = getUserDetailsService().loadUserByUsername(token.getUsername());
|
||||
|
||||
validateUserDetails(user);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.Date;
|
|||
*
|
||||
* @author Luke Taylor
|
||||
* @version $Id$
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface PersistentTokenRepository {
|
||||
|
||||
|
|
|
@ -39,16 +39,12 @@ import java.util.Date;
|
|||
* credentials - not the time period they last logged in via remember-me. The
|
||||
* implementation will only send a remember-me token if the parameter defined by
|
||||
* {@link #setParameter(String)} is present.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* An {@link org.springframework.security.userdetails.UserDetailsService} is required by
|
||||
* this implementation, so that it can construct a valid
|
||||
* <code>Authentication</code> from the returned {@link
|
||||
* org.springframework.security.userdetails.UserDetails}. This is also necessary so that
|
||||
* the user's password is available and can be checked as part of the encoded
|
||||
* cookie.
|
||||
* </p>
|
||||
* <code>Authentication</code> from the returned {@link org.springframework.security.userdetails.UserDetails}.
|
||||
* This is also necessary so that the user's password is available and can be checked as part of the encoded cookie.
|
||||
*
|
||||
* <p>
|
||||
* The cookie encoded by this implementation adopts the following form:
|
||||
|
@ -57,7 +53,6 @@ import java.util.Date;
|
|||
* username + ":" + expiryTime + ":" + Md5Hex(username + ":" + expiryTime + ":" + password + ":" + key)
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* As such, if the user changes their password, any remember-me token will be
|
||||
* invalidated. Equally, the system administrator may invalidate every
|
||||
|
@ -69,17 +64,17 @@ import java.util.Date;
|
|||
* implementation (as we do not want to rely on a database for remember-me
|
||||
* services) and as such high security applications should be aware of this
|
||||
* occasionally undesired disclosure of a valid username.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This is a basic remember-me implementation which is suitable for many
|
||||
* applications. However, we recommend a database-based implementation if you
|
||||
* require a more secure remember-me approach (see {@link PersistentTokenBasedRememberMeServices}).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* By default the tokens will be valid for 14 days from the last successful
|
||||
* authentication attempt. This can be changed using
|
||||
* {@link #setTokenValiditySeconds(int)}.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @version $Id$
|
||||
|
@ -116,8 +111,6 @@ public class TokenBasedRememberMeServices extends AbstractRememberMeServices {
|
|||
|
||||
UserDetails userDetails = getUserDetailsService().loadUserByUsername(cookieTokens[0]);
|
||||
|
||||
validateUserDetails(userDetails);
|
||||
|
||||
// Check signature of token matches remaining details.
|
||||
// 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,
|
||||
|
|
|
@ -259,7 +259,6 @@ public class AbstractRememberMeServicesTests {
|
|||
}
|
||||
|
||||
UserDetails user = getUserDetailsService().loadUserByUsername("joe");
|
||||
validateUserDetails(user);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
|
|
@ -84,8 +84,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||
return tokenValueBase64;
|
||||
}
|
||||
|
||||
public void testAutoLoginIfDoesNotPresentAnyCookies()
|
||||
throws Exception {
|
||||
public void testAutoLoginIfDoesNotPresentAnyCookies() throws Exception {
|
||||
TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
|
||||
services.setKey("key");
|
||||
services.setUserDetailsService(new MockAuthenticationDao(null, true));
|
||||
|
@ -104,8 +103,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||
assertNull(returnedCookie); // shouldn't try to invalidate our cookie
|
||||
}
|
||||
|
||||
public void testAutoLoginIfDoesNotPresentRequiredCookie()
|
||||
throws Exception {
|
||||
public void testAutoLoginIfDoesNotPresentRequiredCookie() throws Exception {
|
||||
TokenBasedRememberMeServices services = new TokenBasedRememberMeServices();
|
||||
services.setKey("key");
|
||||
services.setUserDetailsService(new MockAuthenticationDao(null, true));
|
||||
|
@ -150,8 +148,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||
assertEquals(0, returnedCookie.getMaxAge());
|
||||
}
|
||||
|
||||
public void testAutoLoginIfMissingThreeTokensInCookieValue()
|
||||
throws Exception {
|
||||
public void testAutoLoginIfMissingThreeTokensInCookieValue() throws Exception {
|
||||
UserDetails user = new User("someone", "password", true, true, true, true,
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ABC")});
|
||||
|
||||
|
@ -201,8 +198,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||
assertEquals(0, returnedCookie.getMaxAge());
|
||||
}
|
||||
|
||||
public void testAutoLoginIfSignatureBlocksDoesNotMatchExpectedValue()
|
||||
throws Exception {
|
||||
public void testAutoLoginIfSignatureBlocksDoesNotMatchExpectedValue() throws Exception {
|
||||
UserDetails user = new User("someone", "password", true, true, true, true,
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ABC")});
|
||||
|
||||
|
@ -228,8 +224,7 @@ public class TokenBasedRememberMeServicesTests extends TestCase {
|
|||
assertEquals(0, returnedCookie.getMaxAge());
|
||||
}
|
||||
|
||||
public void testAutoLoginIfTokenDoesNotContainANumberInCookieValue()
|
||||
throws Exception {
|
||||
public void testAutoLoginIfTokenDoesNotContainANumberInCookieValue() throws Exception {
|
||||
UserDetails user = new User("someone", "password", true, true, true, true,
|
||||
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_ABC")});
|
||||
|
||||
|
|
|
@ -23,9 +23,9 @@
|
|||
-->
|
||||
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
|
||||
<!--
|
||||
Uncomment to enable X509 client authentication support
|
||||
Uncomment to enable X509 client authentication support -->
|
||||
<x509 />
|
||||
-->
|
||||
|
||||
<!-- All of this is unnecessary if auto-config="true" -->
|
||||
<form-login />
|
||||
<anonymous />
|
||||
|
@ -68,7 +68,7 @@ Uncomment to authenticate against an embedded LDAP server.
|
|||
<authentication-provider>
|
||||
<password-encoder hash="md5"/>
|
||||
<user-service>
|
||||
<user name="rod" password="a564de63c2d0da68cf47586ee05984d7" authorities="ROLE_SUPERVISOR,ROLE_USER,ROLE_TELLER" />
|
||||
<user name="rod" password="a564de63c2d0da68cf47586ee05984d7" locked="true" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" />
|
||||
<user name="dianne" password="65d15fe9156f9c4bbffd98085992a44e" authorities="ROLE_USER,ROLE_TELLER" />
|
||||
<user name="scott" password="2b58af6dddbd072ed27ffc86725d7d3a" authorities="ROLE_USER" />
|
||||
</user-service>
|
||||
|
|
Loading…
Reference in New Issue