View Javadoc

1   /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
2    *
3    * Licensed under the Apache License, Version 2.0 (the "License");
4    * you may not use this file except in compliance with the License.
5    * You may obtain a copy of the License at
6    *
7    *     http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  
16  package org.acegisecurity.providers.anonymous;
17  
18  import org.acegisecurity.Authentication;
19  
20  import org.acegisecurity.context.SecurityContextHolder;
21  
22  import org.acegisecurity.ui.AuthenticationDetailsSource;
23  import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;
24  
25  import org.acegisecurity.userdetails.memory.UserAttribute;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  
30  import org.springframework.beans.factory.InitializingBean;
31  
32  import org.springframework.util.Assert;
33  
34  import java.io.IOException;
35  
36  import javax.servlet.Filter;
37  import javax.servlet.FilterChain;
38  import javax.servlet.FilterConfig;
39  import javax.servlet.ServletException;
40  import javax.servlet.ServletRequest;
41  import javax.servlet.ServletResponse;
42  import javax.servlet.http.HttpServletRequest;
43  
44  
45  /**
46   * Detects if there is no <code>Authentication</code> object in the <code>SecurityContextHolder</code>,  and
47   * populates it with one if needed.<p><b>Do not use this class directly.</b> Instead configure <code>web.xml</code>
48   * to use the {@link org.acegisecurity.util.FilterToBeanProxy}.</p>
49   *
50   * @author Ben Alex
51   * @version $Id: AnonymousProcessingFilter.java 1496 2006-05-23 13:38:33Z benalex $
52   */
53  public class AnonymousProcessingFilter implements Filter, InitializingBean {
54      //~ Static fields/initializers =====================================================================================
55  
56      private static final Log logger = LogFactory.getLog(AnonymousProcessingFilter.class);
57  
58      //~ Instance fields ================================================================================================
59  
60      private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
61      private String key;
62      private UserAttribute userAttribute;
63      private boolean removeAfterRequest = true;
64  
65      //~ Methods ========================================================================================================
66  
67      public void afterPropertiesSet() throws Exception {
68          Assert.notNull(userAttribute);
69          Assert.hasLength(key);
70      }
71  
72      /**
73       * Enables subclasses to determine whether or not an anonymous authentication token should be setup for
74       * this request. This is useful if anonymous authentication should be allowed only for specific IP subnet ranges
75       * etc.
76       *
77       * @param request to assist the method determine request details
78       *
79       * @return <code>true</code> if the anonymous token should be setup for this request (provided that the request
80       *         doesn't already have some other <code>Authentication</code> inside it), or <code>false</code> if no
81       *         anonymous token should be setup for this request
82       */
83      protected boolean applyAnonymousForThisRequest(ServletRequest request) {
84          return true;
85      }
86  
87      protected Authentication createAuthentication(ServletRequest request) {
88          Assert.isInstanceOf(HttpServletRequest.class, request,
89              "ServletRequest must be an instance of HttpServletRequest");
90  
91          AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key, userAttribute.getPassword(),
92                  userAttribute.getAuthorities());
93          auth.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
94  
95          return auth;
96      }
97  
98      /**
99       * Does nothing - we reply on IoC lifecycle services instead.
100      */
101     public void destroy() {}
102 
103     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
104         throws IOException, ServletException {
105         boolean addedToken = false;
106 
107         if (applyAnonymousForThisRequest(request)) {
108             if (SecurityContextHolder.getContext().getAuthentication() == null) {
109                 SecurityContextHolder.getContext().setAuthentication(createAuthentication(request));
110                 addedToken = true;
111 
112                 if (logger.isDebugEnabled()) {
113                     logger.debug("Populated SecurityContextHolder with anonymous token: '"
114                         + SecurityContextHolder.getContext().getAuthentication() + "'");
115                 }
116             } else {
117                 if (logger.isDebugEnabled()) {
118                     logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '"
119                         + SecurityContextHolder.getContext().getAuthentication() + "'");
120                 }
121             }
122         }
123 
124         try {
125             chain.doFilter(request, response);
126         } finally {
127             if (addedToken && removeAfterRequest
128                 && createAuthentication(request).equals(SecurityContextHolder.getContext().getAuthentication())) {
129                 SecurityContextHolder.getContext().setAuthentication(null);
130             }
131         }
132     }
133 
134     public String getKey() {
135         return key;
136     }
137 
138     public UserAttribute getUserAttribute() {
139         return userAttribute;
140     }
141 
142     /**
143      * Does nothing - we reply on IoC lifecycle services instead.
144      *
145      * @param ignored not used
146      *
147      * @throws ServletException DOCUMENT ME!
148      */
149     public void init(FilterConfig ignored) throws ServletException {}
150 
151     public boolean isRemoveAfterRequest() {
152         return removeAfterRequest;
153     }
154 
155     public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
156         Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
157         this.authenticationDetailsSource = authenticationDetailsSource;
158     }
159 
160     public void setKey(String key) {
161         this.key = key;
162     }
163 
164     /**
165      * Controls whether the filter will remove the Anonymous token after the request is complete. Generally
166      * this is desired to avoid the expense of a session being created by {@link
167      * org.acegisecurity.context.HttpSessionContextIntegrationFilter HttpSessionContextIntegrationFilter} simply to
168      * store the Anonymous authentication token.<p>Defaults to <code>true</code>, being the most optimal and
169      * appropriate option (ie <code>AnonymousProcessingFilter</code> will clear the token at the end of each request,
170      * thus avoiding the session creation overhead in a typical configuration.</p>
171      *
172      * @param removeAfterRequest DOCUMENT ME!
173      */
174     public void setRemoveAfterRequest(boolean removeAfterRequest) {
175         this.removeAfterRequest = removeAfterRequest;
176     }
177 
178     public void setUserAttribute(UserAttribute userAttributeDefinition) {
179         this.userAttribute = userAttributeDefinition;
180     }
181 }