Browse Source

SEC-168: Prevent errors with concurrent session support.

Ben Alex 19 years ago
parent
commit
9d213f46a4

+ 39 - 24
core/src/main/java/org/acegisecurity/providers/anonymous/AnonymousAuthenticationToken.java

@@ -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.
@@ -16,9 +16,8 @@
 package org.acegisecurity.providers.anonymous;
 
 import org.acegisecurity.GrantedAuthority;
-import org.acegisecurity.providers.AbstractAuthenticationToken;
 
-import org.springframework.util.Assert;
+import org.acegisecurity.providers.AbstractAuthenticationToken;
 
 import java.io.Serializable;
 
@@ -33,6 +32,7 @@ public class AnonymousAuthenticationToken extends AbstractAuthenticationToken
     implements Serializable {
     //~ Instance fields ========================================================
 
+    private Object details;
     private Object principal;
     private boolean authenticated;
     private int keyHash;
@@ -50,7 +50,6 @@ public class AnonymousAuthenticationToken extends AbstractAuthenticationToken
      */
     public AnonymousAuthenticationToken(String key, Object principal,
         GrantedAuthority[] authorities) {
-
         super(authorities);
 
         if ((key == null) || ("".equals(key)) || (principal == null)
@@ -62,17 +61,35 @@ public class AnonymousAuthenticationToken extends AbstractAuthenticationToken
 
         this.keyHash = key.hashCode();
         this.principal = principal;
-		this.authenticated = true;
+        this.authenticated = true;
     }
 
     //~ Methods ================================================================
 
-    public void setAuthenticated(boolean isAuthenticated) {
-        this.authenticated = isAuthenticated;
-    }
+    public boolean equals(Object obj) {
+        if (!super.equals(obj)) {
+            return false;
+        }
 
-    public boolean isAuthenticated() {
-        return this.authenticated;
+        if (obj instanceof AnonymousAuthenticationToken) {
+            AnonymousAuthenticationToken test = (AnonymousAuthenticationToken) obj;
+
+            if (this.getKeyHash() != test.getKeyHash()) {
+                return false;
+            }
+
+            if ((this.details == null) && (test.getDetails() != null)) {
+                return false;
+            }
+
+            if ((this.details != null) && (test.getDetails() == null)) {
+                return false;
+            }
+
+            return this.details.equals(test.getDetails());
+        }
+
+        return false;
     }
 
     /**
@@ -84,6 +101,10 @@ public class AnonymousAuthenticationToken extends AbstractAuthenticationToken
         return "";
     }
 
+    public Object getDetails() {
+        return details;
+    }
+
     public int getKeyHash() {
         return this.keyHash;
     }
@@ -92,21 +113,15 @@ public class AnonymousAuthenticationToken extends AbstractAuthenticationToken
         return this.principal;
     }
 
-    public boolean equals(Object obj) {
-        if (!super.equals(obj)) {
-            return false;
-        }
-
-        if (obj instanceof AnonymousAuthenticationToken) {
-            AnonymousAuthenticationToken test = (AnonymousAuthenticationToken) obj;
-
-            if (this.getKeyHash() != test.getKeyHash()) {
-                return false;
-            }
+    public boolean isAuthenticated() {
+        return this.authenticated;
+    }
 
-            return true;
-        }
+    public void setAuthenticated(boolean isAuthenticated) {
+        this.authenticated = isAuthenticated;
+    }
 
-        return false;
+    public void setDetails(Object details) {
+        this.details = details;
     }
 }

+ 69 - 55
core/src/main/java/org/acegisecurity/providers/anonymous/AnonymousProcessingFilter.java

@@ -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.
@@ -16,7 +16,11 @@
 package org.acegisecurity.providers.anonymous;
 
 import org.acegisecurity.Authentication;
+
 import org.acegisecurity.context.SecurityContextHolder;
+
+import org.acegisecurity.ui.WebAuthenticationDetails;
+
 import org.acegisecurity.userdetails.memory.UserAttribute;
 
 import org.apache.commons.logging.Log;
@@ -34,6 +38,7 @@ import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
 
 
 /**
@@ -62,50 +67,39 @@ public class AnonymousProcessingFilter implements Filter, InitializingBean {
 
     //~ Methods ================================================================
 
-    public void setKey(String key) {
-        this.key = key;
-    }
-
-    public String getKey() {
-        return key;
+    public void afterPropertiesSet() throws Exception {
+        Assert.notNull(userAttribute);
+        Assert.hasLength(key);
     }
 
     /**
-     * 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
-     * org.acegisecurity.context.HttpSessionContextIntegrationFilter
-     * HttpSessionContextIntegrationFilter} simply to store the Anonymous
-     * authentication token.
-     * 
-     * <p>
-     * Defaults to <code>true</code>, being the most optimal and appropriate
-     * option (ie <code>AnonymousProcessingFilter</code> will clear the token
-     * at the end of each request, thus avoiding the session creation overhead
-     * in a typical configuration.
-     * </p>
+     * Enables subclasses to determine whether or not an anonymous
+     * authentication token should be setup for this request. This is useful
+     * if anonymous authentication should be allowed only for specific IP
+     * subnet ranges etc.
      *
-     * @param removeAfterRequest DOCUMENT ME!
+     * @param request to assist the method determine request details
+     *
+     * @return <code>true</code> if the anonymous token should be setup for
+     *         this request (provided that the request doesn't already have
+     *         some other <code>Authentication</code> inside it), or
+     *         <code>false</code> if no anonymous token should be setup for
+     *         this request
      */
-    public void setRemoveAfterRequest(boolean removeAfterRequest) {
-        this.removeAfterRequest = removeAfterRequest;
+    protected boolean applyAnonymousForThisRequest(ServletRequest request) {
+        return true;
     }
 
-    public boolean isRemoveAfterRequest() {
-        return removeAfterRequest;
-    }
+    protected Authentication createAuthentication(ServletRequest request) {
+        Assert.isInstanceOf(HttpServletRequest.class, request,
+            "ServletRequest must be an instance of HttpServletRequest");
 
-    public void setUserAttribute(UserAttribute userAttributeDefinition) {
-        this.userAttribute = userAttributeDefinition;
-    }
+        AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key,
+                userAttribute.getPassword(), userAttribute.getAuthorities());
+        auth.setDetails(new WebAuthenticationDetails(
+                (HttpServletRequest) request));
 
-    public UserAttribute getUserAttribute() {
-        return userAttribute;
-    }
-
-    public void afterPropertiesSet() throws Exception {
-        Assert.notNull(userAttribute);
-        Assert.hasLength(key);
+        return auth;
     }
 
     /**
@@ -119,7 +113,8 @@ public class AnonymousProcessingFilter implements Filter, InitializingBean {
 
         if (applyAnonymousForThisRequest(request)) {
             if (SecurityContextHolder.getContext().getAuthentication() == null) {
-                SecurityContextHolder.getContext().setAuthentication(createAuthentication(
+                SecurityContextHolder.getContext()
+                                     .setAuthentication(createAuthentication(
                         request));
                 addedToken = true;
 
@@ -143,13 +138,22 @@ public class AnonymousProcessingFilter implements Filter, InitializingBean {
             chain.doFilter(request, response);
         } finally {
             if (addedToken && removeAfterRequest
-                && createAuthentication(request).equals(SecurityContextHolder.getContext()
-                                                                             .getAuthentication())) {
+                && createAuthentication(request)
+                       .equals(SecurityContextHolder.getContext()
+                                                    .getAuthentication())) {
                 SecurityContextHolder.getContext().setAuthentication(null);
             }
         }
     }
 
+    public String getKey() {
+        return key;
+    }
+
+    public UserAttribute getUserAttribute() {
+        return userAttribute;
+    }
+
     /**
      * Does nothing - we reply on IoC lifecycle services instead.
      *
@@ -159,26 +163,36 @@ public class AnonymousProcessingFilter implements Filter, InitializingBean {
      */
     public void init(FilterConfig ignored) throws ServletException {}
 
+    public boolean isRemoveAfterRequest() {
+        return removeAfterRequest;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
     /**
-     * Enables subclasses to determine whether or not an anonymous
-     * authentication token should be setup for this request. This is useful
-     * if anonymous authentication should be allowed only for specific IP
-     * subnet ranges etc.
-     *
-     * @param request to assist the method determine request details
+     * 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
+     * org.acegisecurity.context.HttpSessionContextIntegrationFilter
+     * HttpSessionContextIntegrationFilter} simply to store the Anonymous
+     * authentication token.
+     * 
+     * <p>
+     * Defaults to <code>true</code>, being the most optimal and appropriate
+     * option (ie <code>AnonymousProcessingFilter</code> will clear the token
+     * at the end of each request, thus avoiding the session creation overhead
+     * in a typical configuration.
+     * </p>
      *
-     * @return <code>true</code> if the anonymous token should be setup for
-     *         this request (provided that the request doesn't already have
-     *         some other <code>Authentication</code> inside it), or
-     *         <code>false</code> if no anonymous token should be setup for
-     *         this request
+     * @param removeAfterRequest DOCUMENT ME!
      */
-    protected boolean applyAnonymousForThisRequest(ServletRequest request) {
-        return true;
+    public void setRemoveAfterRequest(boolean removeAfterRequest) {
+        this.removeAfterRequest = removeAfterRequest;
     }
 
-    protected Authentication createAuthentication(ServletRequest request) {
-        return new AnonymousAuthenticationToken(key,
-            userAttribute.getPassword(), userAttribute.getAuthorities());
+    public void setUserAttribute(UserAttribute userAttributeDefinition) {
+        this.userAttribute = userAttributeDefinition;
     }
 }