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 // Copy of original org.acegisecurity.ui.rememberme.RememberMeProcessingFilter
017 // with a call to RememberMeServices.loginSuccess added
018 // Opened issue http://opensource.atlassian.com/projects/spring/browse/SEC-517 on it
019
020 //package org.acegisecurity.ui.rememberme;
021 package org.trails.security;
022
023 import org.acegisecurity.Authentication;
024 import org.acegisecurity.AuthenticationException;
025 import org.acegisecurity.AuthenticationManager;
026
027 import org.acegisecurity.context.SecurityContextHolder;
028
029 import org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent;
030 import org.acegisecurity.ui.rememberme.NullRememberMeServices;
031 import org.acegisecurity.ui.rememberme.RememberMeServices;
032
033 import org.apache.commons.logging.Log;
034 import org.apache.commons.logging.LogFactory;
035
036 import org.springframework.beans.factory.InitializingBean;
037
038 import org.springframework.context.ApplicationEventPublisher;
039 import org.springframework.context.ApplicationEventPublisherAware;
040
041 import org.springframework.util.Assert;
042
043 import java.io.IOException;
044
045 import javax.servlet.Filter;
046 import javax.servlet.FilterChain;
047 import javax.servlet.FilterConfig;
048 import javax.servlet.ServletException;
049 import javax.servlet.ServletRequest;
050 import javax.servlet.ServletResponse;
051 import javax.servlet.http.HttpServletRequest;
052 import javax.servlet.http.HttpServletResponse;
053
054
055 /**
056 * Detects if there is no <code>Authentication</code> object in the <code>SecurityContext</code>, and populates it
057 * with a remember-me authentication token if a {@link org.acegisecurity.ui.rememberme.RememberMeServices}
058 * implementation so requests.<p>Concrete <code>RememberMeServices</code> implementations will have their {@link
059 * org.acegisecurity.ui.rememberme.RememberMeServices#autoLogin(HttpServletRequest, HttpServletResponse)} method
060 * called by this filter. The <code>Authentication</code> or <code>null</code> returned by that method will be placed
061 * into the <code>SecurityContext</code>. The <code>AuthenticationManager</code> will be used, so that any concurrent
062 * session management or other authentication-specific behaviour can be achieved. This is the same pattern as with
063 * other authentication mechanisms, which call the <code>AuthenticationManager</code> as part of their contract.</p>
064 * <p>If authentication is successful, an {@link
065 * org.acegisecurity.event.authentication.InteractiveAuthenticationSuccessEvent} will be published to the application
066 * context. No events will be published if authentication was unsuccessful, because this would generally be recorded
067 * via an <code>AuthenticationManager</code>-specific application event.</p>
068 * <p><b>Do not use this class directly.</b> Instead configure <code>web.xml</code> to use the {@link
069 * org.acegisecurity.util.FilterToBeanProxy}.</p>
070 *
071 * @author Ben Alex
072 * @version $Id: RememberMeProcessingFilter.java 1496 2006-05-23 13:38:33Z benalex $
073 */
074 public class RememberMeProcessingFilter implements Filter, InitializingBean, ApplicationEventPublisherAware {
075 //~ Static fields/initializers =====================================================================================
076
077 private static final Log logger = LogFactory.getLog(RememberMeProcessingFilter.class);
078
079 //~ Instance fields ================================================================================================
080
081 private ApplicationEventPublisher eventPublisher;
082 private AuthenticationManager authenticationManager;
083 private RememberMeServices rememberMeServices = new NullRememberMeServices();
084
085 //~ Methods ========================================================================================================
086
087 public void afterPropertiesSet() throws Exception {
088 Assert.notNull(rememberMeServices, "RememberMeServices required");
089 Assert.notNull(authenticationManager, "AuthenticationManager required");
090 }
091
092 /**
093 * Does nothing - we rely on IoC lifecycle services instead.
094 */
095 public void destroy() {}
096
097 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
098 throws IOException, ServletException {
099 if (!(request instanceof HttpServletRequest)) {
100 throw new ServletException("Can only process HttpServletRequest");
101 }
102
103 if (!(response instanceof HttpServletResponse)) {
104 throw new ServletException("Can only process HttpServletResponse");
105 }
106
107 HttpServletRequest httpRequest = (HttpServletRequest) request;
108 HttpServletResponse httpResponse = (HttpServletResponse) response;
109
110 Authentication rememberMeAuth = null;
111 if (SecurityContextHolder.getContext().getAuthentication() == null) {
112 rememberMeAuth = rememberMeServices.autoLogin(httpRequest, httpResponse);
113
114 if (rememberMeAuth != null) {
115 // Attempt authenticaton via AuthenticationManager
116 try {
117 authenticationManager.authenticate(rememberMeAuth);
118
119 // Store to SecurityContextHolder
120 SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);
121
122 if (logger.isDebugEnabled()) {
123 logger.debug("SecurityContextHolder populated with remember-me token: '"
124 + SecurityContextHolder.getContext().getAuthentication() + "'");
125 }
126
127 // Fire event
128 if (this.eventPublisher != null) {
129 eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
130 SecurityContextHolder.getContext().getAuthentication(), this.getClass()));
131 }
132 // Notify RememberMeServices so it can for example do rolling cookies
133 rememberMeServices.loginSuccess(httpRequest, httpResponse, rememberMeAuth);
134
135 } catch (AuthenticationException authenticationException) {
136 if (logger.isDebugEnabled()) {
137 logger.debug(
138 "SecurityContextHolder not populated with remember-me token, as AuthenticationManager rejected Authentication returned by RememberMeServices: '"
139 + rememberMeAuth + "'; invalidating remember-me token", authenticationException);
140 }
141
142 rememberMeServices.loginFail(httpRequest, httpResponse);
143 }
144 }
145 chain.doFilter(request, response);
146 } else {
147 if (logger.isDebugEnabled()) {
148 logger.debug("SecurityContextHolder not populated with remember-me token, as it already contained: '"
149 + SecurityContextHolder.getContext().getAuthentication() + "'");
150 }
151
152 chain.doFilter(request, response);
153 }
154 }
155
156 public RememberMeServices getRememberMeServices() {
157 return rememberMeServices;
158 }
159
160 /**
161 * Does nothing - we rely 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 public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
170 this.eventPublisher = eventPublisher;
171 }
172
173 public void setAuthenticationManager(AuthenticationManager authenticationManager) {
174 this.authenticationManager = authenticationManager;
175 }
176
177 public void setRememberMeServices(RememberMeServices rememberMeServices) {
178 this.rememberMeServices = rememberMeServices;
179 }
180 }