All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter Maven / Gradle / Ivy

There is a newer version: 6.2.4
Show newest version
/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.security.web.authentication.preauth;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.log.LogMessage;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;

/**
 * Base class for processing filters that handle pre-authenticated authentication
 * requests, where it is assumed that the principal has already been authenticated by an
 * external system.
 * 

* The purpose is then only to extract the necessary information on the principal from the * incoming request, rather than to authenticate them. External authentication systems may * provide this information via request data such as headers or cookies which the * pre-authentication system can extract. It is assumed that the external system is * responsible for the accuracy of the data and preventing the submission of forged * values. * * Subclasses must implement the {@code getPreAuthenticatedPrincipal()} and * {@code getPreAuthenticatedCredentials()} methods. Subclasses of this filter are * typically used in combination with a {@code PreAuthenticatedAuthenticationProvider}, * which is used to load additional data for the user. This provider will reject null * credentials, so the {@link #getPreAuthenticatedCredentials} method should not return * null for a valid principal. *

* If the security context already contains an {@code Authentication} object (either from * a invocation of the filter or because of some other authentication mechanism), the * filter will do nothing by default. You can force it to check for a change in the * principal by setting the {@link #setCheckForPrincipalChanges(boolean) * checkForPrincipalChanges} property. *

* By default, the filter chain will proceed when an authentication attempt fails in order * to allow other authentication mechanisms to process the request. To reject the * credentials immediately, set the * continueFilterChainOnUnsuccessfulAuthentication flag to false. The exception * raised by the AuthenticationManager will the be re-thrown. Note that this will * not affect cases where the principal returned by {@link #getPreAuthenticatedPrincipal} * is null, when the chain will still proceed as normal. * * @author Luke Taylor * @author Ruud Senden * @author Rob Winch * @author Tadaya Tsuyukubo * @since 2.0 */ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFilterBean implements ApplicationEventPublisherAware { private ApplicationEventPublisher eventPublisher = null; private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); private AuthenticationManager authenticationManager = null; private boolean continueFilterChainOnUnsuccessfulAuthentication = true; private boolean checkForPrincipalChanges; private boolean invalidateSessionOnPrincipalChange = true; private AuthenticationSuccessHandler authenticationSuccessHandler = null; private AuthenticationFailureHandler authenticationFailureHandler = null; private RequestMatcher requiresAuthenticationRequestMatcher = new PreAuthenticatedProcessingRequestMatcher(); /** * Check whether all required properties have been set. */ @Override public void afterPropertiesSet() { try { super.afterPropertiesSet(); } catch (ServletException ex) { // convert to RuntimeException for passivity on afterPropertiesSet signature throw new RuntimeException(ex); } Assert.notNull(this.authenticationManager, "An AuthenticationManager must be set"); } /** * Try to authenticate a pre-authenticated user with Spring Security if the user has * not yet been authenticated. */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (this.requiresAuthenticationRequestMatcher.matches((HttpServletRequest) request)) { if (logger.isDebugEnabled()) { logger.debug(LogMessage .of(() -> "Authenticating " + SecurityContextHolder.getContext().getAuthentication())); } doAuthenticate((HttpServletRequest) request, (HttpServletResponse) response); } else { if (logger.isTraceEnabled()) { logger.trace(LogMessage.format("Did not authenticate since request did not match [%s]", this.requiresAuthenticationRequestMatcher)); } } chain.doFilter(request, response); } /** * Determines if the current principal has changed. The default implementation tries * *

    *
  • If the {@link #getPreAuthenticatedPrincipal(HttpServletRequest)} is a String, * the {@link Authentication#getName()} is compared against the pre authenticated * principal
  • *
  • Otherwise, the {@link #getPreAuthenticatedPrincipal(HttpServletRequest)} is * compared against the {@link Authentication#getPrincipal()} *
* *

* Subclasses can override this method to determine when a principal has changed. *

* @param request * @param currentAuthentication * @return true if the principal has changed, else false */ protected boolean principalChanged(HttpServletRequest request, Authentication currentAuthentication) { Object principal = getPreAuthenticatedPrincipal(request); if ((principal instanceof String) && currentAuthentication.getName().equals(principal)) { return false; } if (principal != null && principal.equals(currentAuthentication.getPrincipal())) { return false; } this.logger.debug(LogMessage.format("Pre-authenticated principal has changed to %s and will be reauthenticated", principal)); return true; } /** * Do the actual authentication for a pre-authenticated user. */ private void doAuthenticate(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { Object principal = getPreAuthenticatedPrincipal(request); if (principal == null) { this.logger.debug("No pre-authenticated principal found in request"); return; } this.logger.debug(LogMessage.format("preAuthenticatedPrincipal = %s, trying to authenticate", principal)); Object credentials = getPreAuthenticatedCredentials(request); try { PreAuthenticatedAuthenticationToken authenticationRequest = new PreAuthenticatedAuthenticationToken( principal, credentials); authenticationRequest.setDetails(this.authenticationDetailsSource.buildDetails(request)); Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest); successfulAuthentication(request, response, authenticationResult); } catch (AuthenticationException ex) { unsuccessfulAuthentication(request, response, ex); if (!this.continueFilterChainOnUnsuccessfulAuthentication) { throw ex; } } } /** * Puts the Authentication instance returned by the authentication * manager into the secure context. */ protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) throws IOException, ServletException { this.logger.debug(LogMessage.format("Authentication success: %s", authResult)); SecurityContextHolder.getContext().setAuthentication(authResult); if (this.eventPublisher != null) { this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass())); } if (this.authenticationSuccessHandler != null) { this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authResult); } } /** * Ensures the authentication object in the secure context is set to null when * authentication fails. *

* Caches the failure exception as a request attribute */ protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { SecurityContextHolder.clearContext(); this.logger.debug("Cleared security context due to exception", failed); request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, failed); if (this.authenticationFailureHandler != null) { this.authenticationFailureHandler.onAuthenticationFailure(request, response, failed); } } /** * @param anApplicationEventPublisher The ApplicationEventPublisher to use */ @Override public void setApplicationEventPublisher(ApplicationEventPublisher anApplicationEventPublisher) { this.eventPublisher = anApplicationEventPublisher; } /** * @param authenticationDetailsSource The AuthenticationDetailsSource to use */ public void setAuthenticationDetailsSource( AuthenticationDetailsSource authenticationDetailsSource) { Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required"); this.authenticationDetailsSource = authenticationDetailsSource; } protected AuthenticationDetailsSource getAuthenticationDetailsSource() { return this.authenticationDetailsSource; } /** * @param authenticationManager The AuthenticationManager to use */ public void setAuthenticationManager(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; } /** * If set to {@code true} (the default), any {@code AuthenticationException} raised by * the {@code AuthenticationManager} will be swallowed, and the request will be * allowed to proceed, potentially using alternative authentication mechanisms. If * {@code false}, authentication failure will result in an immediate exception. * @param shouldContinue set to {@code true} to allow the request to proceed after a * failed authentication. */ public void setContinueFilterChainOnUnsuccessfulAuthentication(boolean shouldContinue) { this.continueFilterChainOnUnsuccessfulAuthentication = shouldContinue; } /** * If set, the pre-authenticated principal will be checked on each request and * compared against the name of the current Authentication object. A check to * determine if {@link Authentication#getPrincipal()} is equal to the principal will * also be performed. If a change is detected, the user will be reauthenticated. * @param checkForPrincipalChanges */ public void setCheckForPrincipalChanges(boolean checkForPrincipalChanges) { this.checkForPrincipalChanges = checkForPrincipalChanges; } /** * If checkForPrincipalChanges is set, and a change of principal is detected, * determines whether any existing session should be invalidated before proceeding to * authenticate the new principal. * @param invalidateSessionOnPrincipalChange false to retain the existing * session. Defaults to true. */ public void setInvalidateSessionOnPrincipalChange(boolean invalidateSessionOnPrincipalChange) { this.invalidateSessionOnPrincipalChange = invalidateSessionOnPrincipalChange; } /** * Sets the strategy used to handle a successful authentication. */ public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) { this.authenticationSuccessHandler = authenticationSuccessHandler; } /** * Sets the strategy used to handle a failed authentication. */ public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { this.authenticationFailureHandler = authenticationFailureHandler; } /** * Sets the request matcher to check whether to proceed the request further. */ public void setRequiresAuthenticationRequestMatcher(RequestMatcher requiresAuthenticationRequestMatcher) { Assert.notNull(requiresAuthenticationRequestMatcher, "requestMatcher cannot be null"); this.requiresAuthenticationRequestMatcher = requiresAuthenticationRequestMatcher; } /** * Override to extract the principal information from the current request */ protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request); /** * Override to extract the credentials (if applicable) from the current request. * Should not return null for a valid principal, though some implementations may * return a dummy value. */ protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request); /** * Request matcher for default auth check logic */ private class PreAuthenticatedProcessingRequestMatcher implements RequestMatcher { @Override public boolean matches(HttpServletRequest request) { Authentication currentUser = SecurityContextHolder.getContext().getAuthentication(); if (currentUser == null) { return true; } if (!AbstractPreAuthenticatedProcessingFilter.this.checkForPrincipalChanges) { return false; } if (!principalChanged(request, currentUser)) { return false; } AbstractPreAuthenticatedProcessingFilter.this.logger .debug("Pre-authenticated principal has changed and will be reauthenticated"); if (AbstractPreAuthenticatedProcessingFilter.this.invalidateSessionOnPrincipalChange) { SecurityContextHolder.clearContext(); HttpSession session = request.getSession(false); if (session != null) { AbstractPreAuthenticatedProcessingFilter.this.logger.debug("Invalidating existing session"); session.invalidate(); request.getSession(); } } return true; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy