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.
+