001: /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
002: *
003: * Licensed under the Apache License, Version 2.0 (the "License");
004: * you may not use this file except in compliance with the License.
005: * You may obtain a copy of the License at
006: *
007: * http://www.apache.org/licenses/LICENSE-2.0
008: *
009: * Unless required by applicable law or agreed to in writing, software
010: * distributed under the License is distributed on an "AS IS" BASIS,
011: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: * See the License for the specific language governing permissions and
013: * limitations under the License.
014: */
015:
016: package org.acegisecurity.providers.anonymous;
017:
018: import org.acegisecurity.Authentication;
019:
020: import org.acegisecurity.context.SecurityContextHolder;
021:
022: import org.acegisecurity.ui.AuthenticationDetailsSource;
023: import org.acegisecurity.ui.AuthenticationDetailsSourceImpl;
024:
025: import org.acegisecurity.userdetails.memory.UserAttribute;
026:
027: import org.apache.commons.logging.Log;
028: import org.apache.commons.logging.LogFactory;
029:
030: import org.springframework.beans.factory.InitializingBean;
031:
032: import org.springframework.util.Assert;
033:
034: import java.io.IOException;
035:
036: import javax.servlet.Filter;
037: import javax.servlet.FilterChain;
038: import javax.servlet.FilterConfig;
039: import javax.servlet.ServletException;
040: import javax.servlet.ServletRequest;
041: import javax.servlet.ServletResponse;
042: import javax.servlet.http.HttpServletRequest;
043:
044: /**
045: * Detects if there is no <code>Authentication</code> object in the <code>SecurityContextHolder</code>, and
046: * populates it with one if needed.<p><b>Do not use this class directly.</b> Instead configure <code>web.xml</code>
047: * to use the {@link org.acegisecurity.util.FilterToBeanProxy}.</p>
048: *
049: * @author Ben Alex
050: * @version $Id: AnonymousProcessingFilter.java 1496 2006-05-23 13:38:33Z benalex $
051: */
052: public class AnonymousProcessingFilter implements Filter,
053: InitializingBean {
054: //~ Static fields/initializers =====================================================================================
055:
056: private static final Log logger = LogFactory
057: .getLog(AnonymousProcessingFilter.class);
058:
059: //~ Instance fields ================================================================================================
060:
061: private AuthenticationDetailsSource authenticationDetailsSource = new AuthenticationDetailsSourceImpl();
062: private String key;
063: private UserAttribute userAttribute;
064: private boolean removeAfterRequest = true;
065:
066: //~ Methods ========================================================================================================
067:
068: public void afterPropertiesSet() throws Exception {
069: Assert.notNull(userAttribute);
070: Assert.hasLength(key);
071: }
072:
073: /**
074: * Enables subclasses to determine whether or not an anonymous authentication token should be setup for
075: * this request. This is useful if anonymous authentication should be allowed only for specific IP subnet ranges
076: * etc.
077: *
078: * @param request to assist the method determine request details
079: *
080: * @return <code>true</code> if the anonymous token should be setup for this request (provided that the request
081: * doesn't already have some other <code>Authentication</code> inside it), or <code>false</code> if no
082: * anonymous token should be setup for this request
083: */
084: protected boolean applyAnonymousForThisRequest(
085: ServletRequest request) {
086: return true;
087: }
088:
089: protected Authentication createAuthentication(ServletRequest request) {
090: Assert
091: .isInstanceOf(HttpServletRequest.class, request,
092: "ServletRequest must be an instance of HttpServletRequest");
093:
094: AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(
095: key, userAttribute.getPassword(), userAttribute
096: .getAuthorities());
097: auth.setDetails(authenticationDetailsSource
098: .buildDetails((HttpServletRequest) request));
099:
100: return auth;
101: }
102:
103: /**
104: * Does nothing - we reply on IoC lifecycle services instead.
105: */
106: public void destroy() {
107: }
108:
109: public void doFilter(ServletRequest request,
110: ServletResponse response, FilterChain chain)
111: throws IOException, ServletException {
112: boolean addedToken = false;
113:
114: if (applyAnonymousForThisRequest(request)) {
115: if (SecurityContextHolder.getContext().getAuthentication() == null) {
116: SecurityContextHolder.getContext().setAuthentication(
117: createAuthentication(request));
118: addedToken = true;
119:
120: if (logger.isDebugEnabled()) {
121: logger
122: .debug("Populated SecurityContextHolder with anonymous token: '"
123: + SecurityContextHolder
124: .getContext()
125: .getAuthentication() + "'");
126: }
127: } else {
128: if (logger.isDebugEnabled()) {
129: logger
130: .debug("SecurityContextHolder not populated with anonymous token, as it already contained: '"
131: + SecurityContextHolder
132: .getContext()
133: .getAuthentication() + "'");
134: }
135: }
136: }
137:
138: try {
139: chain.doFilter(request, response);
140: } finally {
141: if (addedToken
142: && removeAfterRequest
143: && createAuthentication(request).equals(
144: SecurityContextHolder.getContext()
145: .getAuthentication())) {
146: SecurityContextHolder.getContext().setAuthentication(
147: null);
148: }
149: }
150: }
151:
152: public String getKey() {
153: return key;
154: }
155:
156: public UserAttribute getUserAttribute() {
157: return userAttribute;
158: }
159:
160: /**
161: * Does nothing - we reply on IoC lifecycle services instead.
162: *
163: * @param ignored not used
164: *
165: * @throws ServletException DOCUMENT ME!
166: */
167: public void init(FilterConfig ignored) throws ServletException {
168: }
169:
170: public boolean isRemoveAfterRequest() {
171: return removeAfterRequest;
172: }
173:
174: public void setAuthenticationDetailsSource(
175: AuthenticationDetailsSource authenticationDetailsSource) {
176: Assert.notNull(authenticationDetailsSource,
177: "AuthenticationDetailsSource required");
178: this .authenticationDetailsSource = authenticationDetailsSource;
179: }
180:
181: public void setKey(String key) {
182: this .key = key;
183: }
184:
185: /**
186: * Controls whether the filter will remove the Anonymous token after the request is complete. Generally
187: * this is desired to avoid the expense of a session being created by {@link
188: * org.acegisecurity.context.HttpSessionContextIntegrationFilter HttpSessionContextIntegrationFilter} simply to
189: * store the Anonymous authentication token.<p>Defaults to <code>true</code>, being the most optimal and
190: * appropriate option (ie <code>AnonymousProcessingFilter</code> will clear the token at the end of each request,
191: * thus avoiding the session creation overhead in a typical configuration.</p>
192: *
193: * @param removeAfterRequest DOCUMENT ME!
194: */
195: public void setRemoveAfterRequest(boolean removeAfterRequest) {
196: this .removeAfterRequest = removeAfterRequest;
197: }
198:
199: public void setUserAttribute(UserAttribute userAttributeDefinition) {
200: this.userAttribute = userAttributeDefinition;
201: }
202: }
|