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    }