SEC-120: Remember-me to delegate to AuthenticationManager so authentication-specific behaviour (such as concurrent user management) can be applied.

This commit is contained in:
Ben Alex 2006-01-28 01:22:36 +00:00
parent 9062b4c352
commit c8c7c24822
3 changed files with 115 additions and 48 deletions

View File

@ -1,4 +1,4 @@
/* Copyright 2004, 2005 Acegi Technology Pty Limited
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -15,9 +15,13 @@
package org.acegisecurity.ui.rememberme;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.AuthenticationManager;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -54,15 +58,21 @@ import javax.servlet.http.HttpServletResponse;
* org.acegisecurity.ui.rememberme.RememberMeServices#autoLogin(HttpServletRequest,
* HttpServletResponse)} method called by this filter. The
* <code>Authentication</code> or <code>null</code> returned by that method
* will be placed into the <code>SecurityContext</code>.
* will be placed into the <code>SecurityContext</code>. The
* <code>AuthenticationManager</code> will be used, so that any concurrent
* session management or other authentication-specific behaviour can be
* achieved. This is the same pattern as with other authentication mechanisms,
* which call the <code>AuthenticationManager</code> as part of their
* contract.
* </p>
*
* <p>
* If authentication is successful, an {@link
* org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent} will be
* published to the application context. No events will be published if
* authentication was unsuccessful, because this would generally be recorded
* via an <code>AuthenticationManager</code>-specific application event.
* org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent}
* will be published to the application context. No events will be published
* if authentication was unsuccessful, because this would generally be
* recorded via an <code>AuthenticationManager</code>-specific application
* event.
* </p>
*
* <p>
@ -75,7 +85,7 @@ import javax.servlet.http.HttpServletResponse;
* @version $Id$
*/
public class RememberMeProcessingFilter implements Filter, InitializingBean,
ApplicationEventPublisherAware {
ApplicationEventPublisherAware {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(RememberMeProcessingFilter.class);
@ -83,24 +93,14 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean,
//~ Instance fields ========================================================
private ApplicationEventPublisher eventPublisher;
private AuthenticationManager authenticationManager;
private RememberMeServices rememberMeServices = new NullRememberMeServices();
//~ Methods ================================================================
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void setRememberMeServices(RememberMeServices rememberMeServices) {
this.rememberMeServices = rememberMeServices;
}
public RememberMeServices getRememberMeServices() {
return rememberMeServices;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(rememberMeServices);
Assert.notNull(rememberMeServices, "RememberMeServices required");
Assert.notNull(authenticationManager, "AuthenticationManager required");
}
/**
@ -122,11 +122,29 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean,
HttpServletResponse httpResponse = (HttpServletResponse) response;
if (SecurityContextHolder.getContext().getAuthentication() == null) {
Authentication rememberMeAuth =
rememberMeServices.autoLogin(httpRequest, httpResponse);
Authentication rememberMeAuth = rememberMeServices.autoLogin(httpRequest,
httpResponse);
if(rememberMeAuth != null) {
SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);
if (rememberMeAuth != null) {
// Attempt authenticaton via AuthenticationManager
try {
authenticationManager.authenticate(rememberMeAuth);
} catch (AuthenticationException authenticationException) {
if (logger.isDebugEnabled()) {
logger.debug(
"SecurityContextHolder not populated with remember-me token, as AuthenticationManager rejected Authentication returned by RememberMeServices: '"
+ rememberMeAuth
+ "'; invalidating remember-me token",
authenticationException);
}
rememberMeServices.loginFail(httpRequest, httpResponse);
chain.doFilter(request, response);
}
// Store to SecurityContextHolder
SecurityContextHolder.getContext()
.setAuthentication(rememberMeAuth);
if (logger.isDebugEnabled()) {
logger.debug(
@ -138,9 +156,12 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean,
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
SecurityContextHolder.getContext().getAuthentication(),
SecurityContextHolder.getContext()
.getAuthentication(),
this.getClass()));
}
chain.doFilter(request, response);
}
} else {
if (logger.isDebugEnabled()) {
@ -149,9 +170,13 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean,
+ SecurityContextHolder.getContext().getAuthentication()
+ "'");
}
}
chain.doFilter(request, response);
chain.doFilter(request, response);
}
}
public RememberMeServices getRememberMeServices() {
return rememberMeServices;
}
/**
@ -159,6 +184,21 @@ public class RememberMeProcessingFilter implements Filter, InitializingBean,
*
* @param ignored not used
*
* @throws ServletException DOCUMENT ME!
*/
public void init(FilterConfig ignored) throws ServletException {}
public void setApplicationEventPublisher(
ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void setAuthenticationManager(
AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
public void setRememberMeServices(RememberMeServices rememberMeServices) {
this.rememberMeServices = rememberMeServices;
}
}

View File

@ -1,4 +1,4 @@
/* Copyright 2004, 2005 Acegi Technology Pty Limited
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,9 +20,12 @@ import junit.framework.TestCase;
import org.acegisecurity.Authentication;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.MockAuthenticationManager;
import org.acegisecurity.MockFilterConfig;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.context.SecurityContextImpl;
import org.acegisecurity.providers.TestingAuthenticationToken;
import org.springframework.mock.web.MockHttpServletRequest;
@ -59,13 +62,50 @@ public class RememberMeProcessingFilterTests extends TestCase {
//~ Methods ================================================================
private void executeFilterInContainerSimulator(FilterConfig filterConfig,
Filter filter, ServletRequest request, ServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
filter.init(filterConfig);
filter.doFilter(request, response, filterChain);
filter.destroy();
}
public static void main(String[] args) {
junit.textui.TestRunner.run(RememberMeProcessingFilterTests.class);
}
protected void setUp() throws Exception {
super.setUp();
SecurityContextHolder.setContext(new SecurityContextImpl());
}
protected void tearDown() throws Exception {
super.tearDown();
SecurityContextHolder.setContext(new SecurityContextImpl());
}
public void testDetectsAuthenticationManagerProperty()
throws Exception {
RememberMeProcessingFilter filter = new RememberMeProcessingFilter();
filter.setAuthenticationManager(new MockAuthenticationManager());
filter.afterPropertiesSet();
assertTrue(true);
filter.setAuthenticationManager(null);
try {
filter.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
}
public void testDetectsRememberMeServicesProperty()
throws Exception {
RememberMeProcessingFilter filter = new RememberMeProcessingFilter();
filter.setAuthenticationManager(new MockAuthenticationManager());
// check default is NullRememberMeServices
assertEquals(NullRememberMeServices.class,
@ -90,6 +130,7 @@ public class RememberMeProcessingFilterTests extends TestCase {
public void testDoFilterWithNonHttpServletRequestDetected()
throws Exception {
RememberMeProcessingFilter filter = new RememberMeProcessingFilter();
filter.setAuthenticationManager(new MockAuthenticationManager());
try {
filter.doFilter(null, new MockHttpServletResponse(),
@ -104,6 +145,7 @@ public class RememberMeProcessingFilterTests extends TestCase {
public void testDoFilterWithNonHttpServletResponseDetected()
throws Exception {
RememberMeProcessingFilter filter = new RememberMeProcessingFilter();
filter.setAuthenticationManager(new MockAuthenticationManager());
try {
MockHttpServletRequest request = new MockHttpServletRequest();
@ -129,6 +171,7 @@ public class RememberMeProcessingFilterTests extends TestCase {
"password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_REMEMBERED")});
RememberMeProcessingFilter filter = new RememberMeProcessingFilter();
filter.setAuthenticationManager(new MockAuthenticationManager());
filter.setRememberMeServices(new MockRememberMeServices(remembered));
filter.afterPropertiesSet();
@ -149,6 +192,7 @@ public class RememberMeProcessingFilterTests extends TestCase {
"password",
new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_REMEMBERED")});
RememberMeProcessingFilter filter = new RememberMeProcessingFilter();
filter.setAuthenticationManager(new MockAuthenticationManager());
filter.setRememberMeServices(new MockRememberMeServices(remembered));
filter.afterPropertiesSet();
@ -162,24 +206,6 @@ public class RememberMeProcessingFilterTests extends TestCase {
SecurityContextHolder.getContext().getAuthentication());
}
protected void setUp() throws Exception {
super.setUp();
SecurityContextHolder.setContext(new SecurityContextImpl());
}
protected void tearDown() throws Exception {
super.tearDown();
SecurityContextHolder.setContext(new SecurityContextImpl());
}
private void executeFilterInContainerSimulator(FilterConfig filterConfig,
Filter filter, ServletRequest request, ServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
filter.init(filterConfig);
filter.doFilter(request, response, filterChain);
filter.destroy();
}
//~ Inner Classes ==========================================================
private class MockFilterChain implements FilterChain {

View File

@ -90,6 +90,7 @@
</bean>
<bean id="rememberMeProcessingFilter" class="org.acegisecurity.ui.rememberme.RememberMeProcessingFilter">
<property name="authenticationManager"><ref local="authenticationManager"/></property>
<property name="rememberMeServices"><ref local="rememberMeServices"/></property>
</bean>