diff --git a/core/src/main/java/org/acegisecurity/providers/anonymous/AnonymousProcessingFilter.java b/core/src/main/java/org/acegisecurity/providers/anonymous/AnonymousProcessingFilter.java index 4146e5e5d9..d0b7a9ca7f 100644 --- a/core/src/main/java/org/acegisecurity/providers/anonymous/AnonymousProcessingFilter.java +++ b/core/src/main/java/org/acegisecurity/providers/anonymous/AnonymousProcessingFilter.java @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.sf.acegisecurity.providers.anonymous; import net.sf.acegisecurity.Authentication; @@ -38,49 +37,8 @@ import javax.servlet.ServletResponse; /** * Detects if there is no Authentication object in the - * ContextHolder, and populates it with one if needed. - * - *

- * - *

- * In summary, this filter is responsible for processing any request that has a - * HTTP request header of Authorization with an authentication - * scheme of Basic and a Base64-encoded - * username:password token. For example, to authenticate user - * "Aladdin" with password "open sesame" the following header would be - * presented: - *

- * - *

- * Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==. - *

- * - *

- * This filter can be used to provide BASIC authentication services to both - * remoting protocol clients (such as Hessian and SOAP) as well as standard - * user agents (such as Internet Explorer and Netscape). - *

- * - *

- * If authentication is successful, the resulting {@link Authentication} object - * will be placed into the ContextHolder. - *

- * - *

- * If authentication fails, an {@link AuthenticationEntryPoint} implementation - * is called. Usually this should be {@link BasicProcessingFilterEntryPoint}, - * which will prompt the user to authenticate again via BASIC authentication. - *

- * - *

- * Basic authentication is an attractive protocol because it is simple and - * widely deployed. However, it still transmits a password in clear text and - * as such is undesirable in many situations. Digest authentication is also - * provided by Acegi Security and should be used instead of Basic - * authentication wherever possible. See {@link - * net.sf.acegisecurity.ui.digestauth.DigestProcessingFilter}. - *

- * + * SecurityContextHolder, and populates it with one if needed. + * *

* Do not use this class directly. Instead configure * web.xml to use the {@link @@ -91,16 +49,10 @@ import javax.servlet.ServletResponse; * @version $Id$ */ public class AnonymousProcessingFilter implements Filter, InitializingBean { - //~ Static fields/initializers ============================================= - private static final Log logger = LogFactory.getLog(AnonymousProcessingFilter.class); - - //~ Instance fields ======================================================== - private String key; private UserAttribute userAttribute; - - //~ Methods ================================================================ + private boolean removeAfterRequest = true; public void setKey(String key) { this.key = key; @@ -126,32 +78,42 @@ public class AnonymousProcessingFilter implements Filter, InitializingBean { /** * Does nothing - we reply on IoC lifecycle services instead. */ - public void destroy() {} + public void destroy() { + } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + boolean addedToken = false; + if (applyAnonymousForThisRequest(request)) { if (SecurityContextHolder.getContext().getAuthentication() == null) { SecurityContextHolder.getContext().setAuthentication(createAuthentication( request)); + addedToken = true; if (logger.isDebugEnabled()) { logger.debug( - "Replaced SecurityContextHolder with anonymous token: '" - + SecurityContextHolder.getContext().getAuthentication() - + "'"); + "Replaced SecurityContextHolder with anonymous token: '" + + SecurityContextHolder.getContext().getAuthentication() + + "'"); } } else { if (logger.isDebugEnabled()) { logger.debug( - "SecurityContextHolder not replaced with anonymous token, as ContextHolder already contained: '" - + SecurityContextHolder.getContext().getAuthentication() - + "'"); + "SecurityContextHolder not replaced with anonymous token, as ContextHolder already contained: '" + + SecurityContextHolder.getContext().getAuthentication() + + "'"); } } } - chain.doFilter(request, response); + try { + chain.doFilter(request, response); + } finally { + if (addedToken && removeAfterRequest) { + SecurityContextHolder.getContext().setAuthentication(null); + } + } } /** @@ -161,7 +123,8 @@ public class AnonymousProcessingFilter implements Filter, InitializingBean { * * @throws ServletException DOCUMENT ME! */ - public void init(FilterConfig arg0) throws ServletException {} + public void init(FilterConfig arg0) throws ServletException { + } /** * Enables subclasses to determine whether or not an anonymous @@ -185,4 +148,24 @@ public class AnonymousProcessingFilter implements Filter, InitializingBean { return new AnonymousAuthenticationToken(key, userAttribute.getPassword(), userAttribute.getAuthorities()); } + + public boolean isRemoveAfterRequest() { + return removeAfterRequest; + } + + /** + * Controls whether the filter will remove the Anonymous token + * after the request is complete. Generally this is desired to + * avoid the expense of a session being created by + * {@link net.sf.acegisecurity.context.HttpSessionContextIntegrationFilter} simply + * to store the Anonymous authentication token. + * + *

Defaults to true, + * being the most optimal and appropriate option (ie AnonymousProcessingFilter + * will clear the token at the end of each request, thus avoiding the session creation + * overhead in a typical configuration. + */ + public void setRemoveAfterRequest(boolean removeAfterRequest) { + this.removeAfterRequest = removeAfterRequest; + } } diff --git a/core/src/test/java/org/acegisecurity/providers/anonymous/AnonymousProcessingFilterTests.java b/core/src/test/java/org/acegisecurity/providers/anonymous/AnonymousProcessingFilterTests.java index a464df9134..a94015fc35 100644 --- a/core/src/test/java/org/acegisecurity/providers/anonymous/AnonymousProcessingFilterTests.java +++ b/core/src/test/java/org/acegisecurity/providers/anonymous/AnonymousProcessingFilterTests.java @@ -12,7 +12,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.sf.acegisecurity.providers.anonymous; import junit.framework.TestCase; @@ -46,8 +45,6 @@ import javax.servlet.ServletResponse; * @version $Id$ */ public class AnonymousProcessingFilterTests extends TestCase { - //~ Constructors =========================================================== - public AnonymousProcessingFilterTests() { super(); } @@ -56,8 +53,6 @@ public class AnonymousProcessingFilterTests extends TestCase { super(arg0); } - //~ Methods ================================================================ - public static void main(String[] args) { junit.textui.TestRunner.run(AnonymousProcessingFilterTests.class); } @@ -98,10 +93,13 @@ public class AnonymousProcessingFilterTests extends TestCase { AnonymousProcessingFilter filter = new AnonymousProcessingFilter(); filter.setKey("qwerty"); filter.setUserAttribute(user); + assertTrue(filter.isRemoveAfterRequest()); filter.afterPropertiesSet(); assertEquals("qwerty", filter.getKey()); assertEquals(user, filter.getUserAttribute()); + filter.setRemoveAfterRequest(false); + assertFalse(filter.isRemoveAfterRequest()); } public void testOperationWhenAuthenticationExistsInContextHolder() @@ -109,7 +107,7 @@ public class AnonymousProcessingFilterTests extends TestCase { // Put an Authentication object into the ContextHolder Authentication originalAuth = new TestingAuthenticationToken("user", "password", - new GrantedAuthority[] {new GrantedAuthorityImpl("ROLE_A")}); + new GrantedAuthority[] { new GrantedAuthorityImpl("ROLE_A") }); SecurityContextHolder.getContext().setAuthentication(originalAuth); // Setup our filter correctly @@ -133,7 +131,7 @@ public class AnonymousProcessingFilterTests extends TestCase { SecurityContextHolder.getContext().getAuthentication()); } - public void testOperationWhenNoAuthenticationInContextHolder() + public void testOperationWhenNoAuthenticationInSecurityContextHolder() throws Exception { UserAttribute user = new UserAttribute(); user.setPassword("anonymousUsername"); @@ -142,6 +140,7 @@ public class AnonymousProcessingFilterTests extends TestCase { AnonymousProcessingFilter filter = new AnonymousProcessingFilter(); filter.setKey("qwerty"); filter.setUserAttribute(user); + filter.setRemoveAfterRequest(false); // set to non-default value filter.afterPropertiesSet(); MockHttpServletRequest request = new MockHttpServletRequest(); @@ -154,6 +153,13 @@ public class AnonymousProcessingFilterTests extends TestCase { assertEquals("anonymousUsername", auth.getPrincipal()); assertEquals(new GrantedAuthorityImpl("ROLE_ANONYMOUS"), auth.getAuthorities()[0]); + SecurityContextHolder.getContext().setAuthentication(null); // so anonymous fires again + + // Now test operation if we have removeAfterRequest = true + filter.setRemoveAfterRequest(true); // set to default value + executeFilterInContainerSimulator(new MockFilterConfig(), filter, + request, new MockHttpServletResponse(), new MockFilterChain(true)); + assertNull(SecurityContextHolder.getContext().getAuthentication()); } protected void setUp() throws Exception { @@ -174,8 +180,6 @@ public class AnonymousProcessingFilterTests extends TestCase { filter.destroy(); } - //~ Inner Classes ========================================================== - private class MockFilterChain implements FilterChain { private boolean expectToProceed; diff --git a/doc/xdocs/upgrade/upgrade-080-090.html b/doc/xdocs/upgrade/upgrade-080-090.html index ac1b64f913..ecd01fb763 100644 --- a/doc/xdocs/upgrade/upgrade-080-090.html +++ b/doc/xdocs/upgrade/upgrade-080-090.html @@ -40,6 +40,12 @@ applications: authentication exception directions. See the AbstractProcessingFilter JavaDocs to learn more.

+

  • AnonymousProcessingFilter now has a removeAfterRequest property, which defaults to true. This + will cause the anonymous authentication token to be set to null at the end of each request, thus + avoiding the expense of creating a HttpSession in HttpSessionContextIntegrationFilter. You may + set this property to false if you would like the anoymous authentication token to be preserved, + which would be an unusual requirement.

  • + diff --git a/project.xml b/project.xml index d7bfd53ebc..e928b2ef09 100644 --- a/project.xml +++ b/project.xml @@ -147,6 +147,9 @@ Paulo Neves + + Mike Perham +