View Javadoc

1   package org.springframework.security.ui.preauth;
2   
3   import java.io.IOException;
4   
5   import javax.servlet.FilterChain;
6   import javax.servlet.ServletException;
7   import javax.servlet.http.HttpServletRequest;
8   import javax.servlet.http.HttpServletResponse;
9   
10  import org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationToken;
11  import org.springframework.security.AuthenticationManager;
12  import org.springframework.security.Authentication;
13  import org.springframework.security.AuthenticationException;
14  import org.springframework.security.event.authentication.InteractiveAuthenticationSuccessEvent;
15  import org.springframework.security.ui.AuthenticationDetailsSource;
16  import org.springframework.security.ui.WebAuthenticationDetailsSource;
17  import org.springframework.security.ui.AbstractProcessingFilter;
18  import org.springframework.security.ui.SpringSecurityFilter;
19  import org.springframework.security.context.SecurityContextHolder;
20  import org.springframework.beans.factory.InitializingBean;
21  import org.springframework.context.ApplicationEventPublisher;
22  import org.springframework.context.ApplicationEventPublisherAware;
23  import org.springframework.util.Assert;
24  
25  /**
26   * Base class for processing filters that handle pre-authenticated authentication requests. Subclasses must implement
27   * the getPreAuthenticatedPrincipal() and getPreAuthenticatedCredentials() methods.
28   * <p>
29   * By default, the filter chain will proceed when an authentication attempt fails in order to allow other 
30   * authentication mechanisms to process the request. To reject the credentials immediately, set the
31   * <tt>continueFilterChainOnUnsuccessfulAuthentication</tt> flag to false. The exception raised by the
32   * <tt>AuthenticationManager</tt> will the be re-thrown. Note that this will not affect cases where the principal
33   * returned by {@link #getPreAuthenticatedPrincipal} is null, when the chain will still proceed as normal.
34   * 
35   *
36   * @author Luke Taylor
37   * @author Ruud Senden
38   * @since 2.0
39   */
40  public abstract class AbstractPreAuthenticatedProcessingFilter extends SpringSecurityFilter implements
41          InitializingBean, ApplicationEventPublisherAware {
42  
43      private ApplicationEventPublisher eventPublisher = null;
44  
45      private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
46  
47      private AuthenticationManager authenticationManager = null;
48      
49      private boolean continueFilterChainOnUnsuccessfulAuthentication = true;
50  
51      /**
52       * Check whether all required properties have been set.
53       */
54      public void afterPropertiesSet() throws Exception {
55          Assert.notNull(authenticationManager, "An AuthenticationManager must be set");
56      }
57  
58      /**
59       * Try to authenticate a pre-authenticated user with Spring Security if the user has not yet been authenticated.
60       */
61      public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
62          if (logger.isDebugEnabled()) {
63              logger.debug("Checking secure context token: " + SecurityContextHolder.getContext().getAuthentication());
64          }
65  
66          if (SecurityContextHolder.getContext().getAuthentication() == null) {
67              doAuthenticate(request, response);
68          }
69          filterChain.doFilter(request, response);
70      }
71  
72      /**
73       * Do the actual authentication for a pre-authenticated user.
74       */
75      private void doAuthenticate(HttpServletRequest request, HttpServletResponse response) {
76          Authentication authResult = null;
77  
78          Object principal = getPreAuthenticatedPrincipal(request);
79          Object credentials = getPreAuthenticatedCredentials(request);
80  
81          if (principal == null) {
82              if (logger.isDebugEnabled()) {
83                  logger.debug("No pre-authenticated principal found in request");
84              }
85  
86              return;            
87          }
88  
89          if (logger.isDebugEnabled()) {
90              logger.debug("preAuthenticatedPrincipal = " + principal + ", trying to authenticate");
91          }
92  
93          try {
94              PreAuthenticatedAuthenticationToken authRequest = new PreAuthenticatedAuthenticationToken(principal, credentials);
95              authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
96              authResult = authenticationManager.authenticate(authRequest);
97              successfulAuthentication(request, response, authResult);
98          } catch (AuthenticationException failed) {
99              unsuccessfulAuthentication(request, response, failed);
100             
101             if (!continueFilterChainOnUnsuccessfulAuthentication) {
102                 throw failed;
103             }
104         }
105     }
106 
107     /**
108      * Puts the <code>Authentication</code> instance returned by the
109      * authentication manager into the secure context.
110      */
111     protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
112         if (logger.isDebugEnabled()) {
113             logger.debug("Authentication success: " + authResult);
114         }
115         SecurityContextHolder.getContext().setAuthentication(authResult);
116         // Fire event
117         if (this.eventPublisher != null) {
118             eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
119         }
120     }
121 
122     /**
123      * Ensures the authentication object in the secure context is set to null
124      * when authentication fails.
125      */
126     protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
127         SecurityContextHolder.clearContext();
128 
129         if (logger.isDebugEnabled()) {
130             logger.debug("Cleared security context due to exception", failed);
131         }
132         request.getSession().setAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY, failed);
133     }
134 
135     /**
136      * @param anApplicationEventPublisher
137      *            The ApplicationEventPublisher to use
138      */
139     public void setApplicationEventPublisher(ApplicationEventPublisher anApplicationEventPublisher) {
140         this.eventPublisher = anApplicationEventPublisher;
141     }
142 
143     /**
144      * @param authenticationDetailsSource
145      *            The AuthenticationDetailsSource to use
146      */
147     public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
148         Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
149         this.authenticationDetailsSource = authenticationDetailsSource;
150     }
151 
152     /**
153      * @param authenticationManager
154      *            The AuthenticationManager to use
155      */
156     public void setAuthenticationManager(AuthenticationManager authenticationManager) {
157         this.authenticationManager = authenticationManager;
158     }
159     
160     public void setContinueFilterChainOnUnsuccessfulAuthentication(boolean shouldContinue) {
161         continueFilterChainOnUnsuccessfulAuthentication = shouldContinue;
162     }
163 
164     /**
165      * Override to extract the principal information from the current request 
166      */
167     protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request);
168 
169     /**
170      * Override to extract the credentials (if applicable) from the current request. Some implementations
171      * may return a dummy value.
172      */    
173     protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request);
174 }