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.springframework.security.providers.anonymous;
17  
18  import org.springframework.security.Authentication;
19  
20  import org.springframework.security.context.SecurityContextHolder;
21  
22  import org.springframework.security.ui.AuthenticationDetailsSource;
23  import org.springframework.security.ui.WebAuthenticationDetailsSource;
24  import org.springframework.security.ui.FilterChainOrder;
25  import org.springframework.security.ui.SpringSecurityFilter;
26  import org.springframework.security.userdetails.memory.UserAttribute;
27  import org.springframework.beans.factory.InitializingBean;
28  import org.springframework.util.Assert;
29  
30  import java.io.IOException;
31  
32  import javax.servlet.FilterChain;
33  import javax.servlet.ServletException;
34  import javax.servlet.http.HttpServletRequest;
35  import javax.servlet.http.HttpServletResponse;
36  
37  
38  /**
39   * Detects if there is no <code>Authentication</code> object in the <code>SecurityContextHolder</code>,  and
40   * populates it with one if needed.
41   *
42   * @author Ben Alex
43   * @version $Id: AnonymousProcessingFilter.java 2800 2008-03-25 14:51:34Z luke_t $
44   */
45  public class AnonymousProcessingFilter  extends SpringSecurityFilter  implements InitializingBean {
46  
47      //~ Instance fields ================================================================================================
48  
49      private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
50      private String key;
51      private UserAttribute userAttribute;
52      private boolean removeAfterRequest = true;
53  
54      //~ Methods ========================================================================================================
55  
56      public void afterPropertiesSet() throws Exception {
57          Assert.notNull(userAttribute);
58          Assert.hasLength(key);
59      }
60  
61      /**
62       * Enables subclasses to determine whether or not an anonymous authentication token should be setup for
63       * this request. This is useful if anonymous authentication should be allowed only for specific IP subnet ranges
64       * etc.
65       *
66       * @param request to assist the method determine request details
67       *
68       * @return <code>true</code> if the anonymous token should be setup for this request (provided that the request
69       *         doesn't already have some other <code>Authentication</code> inside it), or <code>false</code> if no
70       *         anonymous token should be setup for this request
71       */
72      protected boolean applyAnonymousForThisRequest(HttpServletRequest request) {
73          return true;
74      }
75  
76      protected Authentication createAuthentication(HttpServletRequest request) {
77          AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key, userAttribute.getPassword(),
78                  userAttribute.getAuthorities());
79          auth.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
80  
81          return auth;
82      }
83  
84      protected void doFilterHttp(HttpServletRequest request,HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
85          boolean addedToken = false;
86  
87          if (applyAnonymousForThisRequest(request)) {
88              if (SecurityContextHolder.getContext().getAuthentication() == null) {
89                  SecurityContextHolder.getContext().setAuthentication(createAuthentication(request));
90                  addedToken = true;
91  
92                  if (logger.isDebugEnabled()) {
93                      logger.debug("Populated SecurityContextHolder with anonymous token: '"
94                          + SecurityContextHolder.getContext().getAuthentication() + "'");
95                  }
96              } else {
97                  if (logger.isDebugEnabled()) {
98                      logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '"
99                          + SecurityContextHolder.getContext().getAuthentication() + "'");
100                 }
101             }
102         }
103 
104         try {
105             chain.doFilter(request, response);
106         } finally {
107             if (addedToken && removeAfterRequest
108                 && createAuthentication(request).equals(SecurityContextHolder.getContext().getAuthentication())) {
109                 SecurityContextHolder.getContext().setAuthentication(null);
110             }
111         }
112     }
113 
114     public int getOrder() {
115         return FilterChainOrder.ANONYMOUS_FILTER;
116     }
117 
118     public String getKey() {
119         return key;
120     }
121 
122     public UserAttribute getUserAttribute() {
123         return userAttribute;
124     }
125 
126     public boolean isRemoveAfterRequest() {
127         return removeAfterRequest;
128     }
129 
130     public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
131         Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
132         this.authenticationDetailsSource = authenticationDetailsSource;
133     }
134 
135     public void setKey(String key) {
136         this.key = key;
137     }
138 
139     /**
140      * Controls whether the filter will remove the Anonymous token after the request is complete. Generally
141      * this is desired to avoid the expense of a session being created by {@link
142      * org.springframework.security.context.HttpSessionContextIntegrationFilter HttpSessionContextIntegrationFilter} simply to
143      * store the Anonymous authentication token.<p>Defaults to <code>true</code>, being the most optimal and
144      * appropriate option (ie <code>AnonymousProcessingFilter</code> will clear the token at the end of each request,
145      * thus avoiding the session creation overhead in a typical configuration.</p>
146      *
147      * @param removeAfterRequest DOCUMENT ME!
148      */
149     public void setRemoveAfterRequest(boolean removeAfterRequest) {
150         this.removeAfterRequest = removeAfterRequest;
151     }
152 
153     public void setUserAttribute(UserAttribute userAttributeDefinition) {
154         this.userAttribute = userAttributeDefinition;
155     }
156 }