1
0
mirror of synced 2026-08-05 09:47:05 +00:00

SEC-1160: Renaming of authentication filters and entry points and associated doc changes

This commit is contained in:
Luke Taylor
2009-05-12 05:37:11 +00:00
parent 5a03e842bd
commit a8215fa2cb
53 changed files with 1126 additions and 1061 deletions
@@ -38,7 +38,7 @@ public interface AuthenticationEntryPoint {
* Commences an authentication scheme.
* <p>
* <code>ExceptionTranslationFilter</code> will populate the <code>HttpSession</code> attribute named
* <code>AbstractProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY</code> with the requested target URL before
* <code>AbstractAuthenticationProcessingFilter.SPRING_SECURITY_SAVED_REQUEST_KEY</code> with the requested target URL before
* calling this method.
* <p>
* Implementations should modify the headers on the <code>ServletResponse</code> as necessary to
@@ -206,7 +206,7 @@ public class ExceptionTranslationFilter extends SpringSecurityFilter implements
SavedRequest savedRequest = new SavedRequest(request, portResolver);
if (createSessionAllowed || request.getSession(false) != null) {
// Store the HTTP request itself. Used by AbstractProcessingFilter
// Store the HTTP request itself. Used by AbstractAuthenticationProcessingFilter
// for redirection after successful authentication (SEC-29)
request.getSession().setAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY, savedRequest);
logger.debug("SavedRequest added to Session: " + savedRequest);
@@ -0,0 +1,439 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* http://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;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.concurrent.SessionRegistry;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.SpringSecurityFilter;
import org.springframework.security.web.session.SessionUtils;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
/**
* Abstract processor of browser-based HTTP-based authentication requests.
*
* <h3>Authentication Process</h3>
*
* The filter requires that you set the <tt>authenticationManager</tt> property. An <tt>AuthenticationManager</tt> is
* required to process the authentication request tokens created by implementing classes.
* <p>
* This filter will intercept a request and attempt to perform authentication from that request if
* the request URL matches the value of the <tt>filterProcessesUrl</tt> property. This behaviour can modified by
* overriding the method {@link #requiresAuthentication(HttpServletRequest, HttpServletResponse) requiresAuthentication}.
* <p>
* Authentication is performed by the {@link #attemptAuthentication(HttpServletRequest, HttpServletResponse)
* attemptAuthentication} method, which must be implemented by subclasses.
*
* <h4>Authentication Success</h4>
*
* If authentication is successful, the resulting {@link Authentication} object will be placed into the
* <code>SecurityContext</code> for the current thread, which is guaranteed to have already been created by an earlier
* filter. The configured {@link #setAuthenticationSuccessHandler(AuthenticationSuccessHandler) AuthenticationSuccessHandler} will
* then be called to take the redirect to the appropriate destination after a successful login. The default behaviour
* is implemented in a {@link SavedRequestAwareAuthenticationSuccessHandler} which will make use of any
* <tt>SavedRequest</tt> set by the <tt>ExceptionTranslationFilter</tt> and redirect the user to the URL contained
* therein. Otherwise it will redirect to the webapp root "/". You can customize this behaviour by injecting a
* differently configured instance of this class, or by using a different implementation.
* <p>
* See the {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse, Authentication)
* successfulAuthentication} method for more information.
*
* <h4>Authentication Failure</h4>
*
* If authentication fails, the resulting <tt>AuthenticationException</tt> will be placed into the <tt>HttpSession</tt>
* with the attribute defined by {@link #SPRING_SECURITY_LAST_EXCEPTION_KEY}. It will then delegate to the configured
* {@link AuthenticationFailureHandler} to allow the failure information to be conveyed to the client.
* The default implementation is {@link SimpleUrlAuthenticationFailureHandler}, which sends a 401 error code to the
* client. It may also be configured with a failure URL as an alternative. Again you can inject whatever
* behaviour you require here.
*
* <h4>Event Publication</h4>
*
* If authentication is successful, an
* {@link org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent
* InteractiveAuthenticationSuccessEvent} will be published via the application context. No events will be published if
* authentication was unsuccessful, because this would generally be recorded via an
* <tt>AuthenticationManager</tt>-specific application event.
* <p>
* The filter has an optional attribute <tt>invalidateSessionOnSuccessfulAuthentication</tt> that will invalidate
* the current session on successful authentication. This is to protect against session fixation attacks (see
* <a href="http://en.wikipedia.org/wiki/Session_fixation">this Wikipedia article</a> for more information).
* The behaviour is turned off by default. Additionally there is a property <tt>migrateInvalidatedSessionAttributes</tt>
* which tells if on session invalidation we are to migrate all session attributes from the old session to a newly
* created one. This is turned on by default, but not used unless <tt>invalidateSessionOnSuccessfulAuthentication</tt>
* is true. If you are using this feature in combination with concurrent session control, you should set the
* <tt>sessionRegistry</tt> property to make sure that the session information is updated consistently.
*
* @author Ben Alex
* @version $Id$
*/
public abstract class AbstractAuthenticationProcessingFilter extends SpringSecurityFilter implements InitializingBean,
ApplicationEventPublisherAware, MessageSourceAware {
//~ Static fields/initializers =====================================================================================
public static final String SPRING_SECURITY_LAST_EXCEPTION_KEY = "SPRING_SECURITY_LAST_EXCEPTION";
//~ Instance fields ================================================================================================
protected ApplicationEventPublisher eventPublisher;
protected AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
private AuthenticationManager authenticationManager;
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
/*
* Delay use of NullRememberMeServices until initialization so that namespace has a chance to inject
* the RememberMeServices implementation into custom implementations.
*/
private RememberMeServices rememberMeServices = null;
/**
* The URL destination that this filter intercepts and processes (usually
* something like <code>/j_spring_security_check</code>)
*/
private String filterProcessesUrl;
private boolean continueChainBeforeSuccessfulAuthentication = false;
/**
* Tells if we on successful authentication should invalidate the
* current session. This is a common guard against session fixation attacks.
* Defaults to <code>false</code>.
*/
private boolean invalidateSessionOnSuccessfulAuthentication = false;
/**
* If {@link #invalidateSessionOnSuccessfulAuthentication} is true, this
* flag indicates that the session attributes of the session to be invalidated
* are to be migrated to the new session. Defaults to <code>true</code> since
* nothing will happen unless {@link #invalidateSessionOnSuccessfulAuthentication}
* is true.
*/
private boolean migrateInvalidatedSessionAttributes = true;
private boolean allowSessionCreation = true;
private SessionRegistry sessionRegistry;
private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
//~ Constructors ===================================================================================================
/**
* @param defaultFilterProcessesUrl the default value for <tt>filterProcessesUrl</tt>.
*/
protected AbstractAuthenticationProcessingFilter(String defaultFilterProcessesUrl) {
this.filterProcessesUrl = defaultFilterProcessesUrl;
}
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL");
Assert.notNull(authenticationManager, "authenticationManager must be specified");
if (rememberMeServices == null) {
rememberMeServices = new NullRememberMeServices();
}
}
/**
* Invokes the {@link #requiresAuthentication(HttpServletRequest, HttpServletResponse) requiresAuthentication}
* method to determine whether the request is for authentication and should be handled by this filter.
* If it is an authentication request, the
* {@link #attemptAuthentication(HttpServletRequest, HttpServletResponse) attemptAuthentication} will be invoked
* to perform the authentication. There are then three possible outcomes:
* <ol>
* <li>An <tt>Authentication</tt> object is returned.
* The {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse, Authentication)
* successfulAuthentication} method will be invoked</li>
* <li>An <tt>AuthenticationException</tt> occurs during authentication.
* The {@link #unSuccessfulAuthentication(HttpServletRequest, HttpServletResponse, Authentication)
* unSuccessfulAuthentication} method will be invoked</li>
* <li>Null is returned, indicating that the authentication process is incomplete.
* The method will then return immediately, assuming that the subclass has done any necessary work (such as
* redirects) to continue the authentication process. The assumption is that a later request will be received
* by this method where the returned <tt>Authentication</tt> object is not null.
* </ol>
*/
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
try {
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// return immediately as subclass has indicated that it hasn't completed authentication
return;
}
}
catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(request, response, failed);
return;
}
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, authResult);
}
/**
* Indicates whether this filter should attempt to process a login request for the current invocation.
* <p>
* It strips any parameters from the "path" section of the request URL (such
* as the jsessionid parameter in
* <em>http://host/myapp/index.html;jsessionid=blah</em>) before matching
* against the <code>filterProcessesUrl</code> property.
* <p>
* Subclasses may override for special requirements, such as Tapestry integration.
*
* @return <code>true</code> if the filter should attempt authentication, <code>false</code> otherwise.
*/
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
String uri = request.getRequestURI();
int pathParamIndex = uri.indexOf(';');
if (pathParamIndex > 0) {
// strip everything after the first semi-colon
uri = uri.substring(0, pathParamIndex);
}
if ("".equals(request.getContextPath())) {
return uri.endsWith(filterProcessesUrl);
}
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
}
/**
* Performs actual authentication.
* <p>
* The implementation should do one of the following:
* <ol>
* <li>Return a populated authentication token for the authenticated user, indicating successful authentication</li>
* <li>Return null, indicating that the authentication process is still in progress. Before returning, the
* implementation should perform any additional work required to complete the process.</li>
* <li>Throw an <tt>AuthenticationException</tt> if the authentication process fails</li>
* </ol>
*
* @param request from which to extract parameters and perform the authentication
* @param response the response, which may be needed if the implementation has to do a redirect as part of a
* multi-stage authentication process (such as OpenID).
*
* @return the authenticated user token, or null if authentication is incomplete.
*
* @throws AuthenticationException if authentication fails.
*/
public abstract Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException;
/**
* Default behaviour for successful authentication.
* <ol>
* <li>Sets the successful <tt>Authentication</tt> object on the {@link SecurityContextHolder}</li>
* <li>Performs any configured session migration behaviour</li>
* <li>Informs the configured <tt>RememberMeServices</tt> of the successful login</li>
* <li>Fires an {@link InteractiveAuthenticationSuccessEvent} via the configured
* <tt>ApplicationEventPublisher</tt></li>
* <li>Delegates additional behaviour to the {@link AuthenticationSuccessHandler}.</li>
* </ol>
*
* @param authResult the object returned from the <tt>attemptAuthentication</tt> method.
*/
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authResult) throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
if (invalidateSessionOnSuccessfulAuthentication) {
SessionUtils.startNewSessionIfRequired(request, migrateInvalidatedSessionAttributes, sessionRegistry);
}
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
successHandler.onAuthenticationSuccess(request, response, authResult);
}
/**
* Default behaviour for unsuccessful authentication.
* <ol>
* <li>Clears the {@link SecurityContextHolder}</li>
* <li>Stores the exception in the session (if it exists or <tt>allowSesssionCreation</tt> is set to <tt>true</tt>)</li>
* <li>Informs the configured <tt>RememberMeServices</tt> of the failed login</li>
* <li>Delegates additional behaviour to the {@link AuthenticationFailureHandler}.</li>
* </ol>
*/
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {
SecurityContextHolder.clearContext();
if (logger.isDebugEnabled()) {
logger.debug("Authentication request failed: " + failed.toString());
logger.debug("Updated SecurityContextHolder to contain null Authentication");
logger.debug("Delegating to authentication failure handler" + failureHandler);
}
try {
HttpSession session = request.getSession(false);
if (session != null || allowSessionCreation) {
request.getSession().setAttribute(SPRING_SECURITY_LAST_EXCEPTION_KEY, failed);
}
}
catch (Exception ignored) {
}
rememberMeServices.loginFail(request, response);
failureHandler.onAuthenticationFailure(request, response, failed);
}
protected AuthenticationManager getAuthenticationManager() {
return authenticationManager;
}
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
public String getFilterProcessesUrl() {
return filterProcessesUrl;
}
public void setFilterProcessesUrl(String filterProcessesUrl) {
this.filterProcessesUrl = filterProcessesUrl;
}
public RememberMeServices getRememberMeServices() {
return rememberMeServices;
}
public void setRememberMeServices(RememberMeServices rememberMeServices) {
this.rememberMeServices = rememberMeServices;
}
/**
* Indicates if the filter chain should be continued prior to delegation to
* {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse,
* Authentication)}, which may be useful in certain environment (such as
* Tapestry applications). Defaults to <code>false</code>.
*/
public void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {
this.continueChainBeforeSuccessfulAuthentication = continueChainBeforeSuccessfulAuthentication;
}
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
this.authenticationDetailsSource = authenticationDetailsSource;
}
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
public void setInvalidateSessionOnSuccessfulAuthentication(boolean invalidateSessionOnSuccessfulAuthentication) {
this.invalidateSessionOnSuccessfulAuthentication = invalidateSessionOnSuccessfulAuthentication;
}
public void setMigrateInvalidatedSessionAttributes(boolean migrateInvalidatedSessionAttributes) {
this.migrateInvalidatedSessionAttributes = migrateInvalidatedSessionAttributes;
}
public AuthenticationDetailsSource getAuthenticationDetailsSource() {
// Required due to SEC-310
return authenticationDetailsSource;
}
protected boolean getAllowSessionCreation() {
return allowSessionCreation;
}
public void setAllowSessionCreation(boolean allowSessionCreation) {
this.allowSessionCreation = allowSessionCreation;
}
/**
* The session registry needs to be set if session fixation attack protection is in use (and concurrent
* session control is enabled).
*/
public void setSessionRegistry(SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
}
/**
* Sets the strategy used to handle a successful authentication.
* By default a {@link SavedRequestAwareAuthenticationSuccessHandler} is used.
*/
public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler successHandler) {
Assert.notNull(successHandler, "successHandler cannot be null");
this.successHandler = successHandler;
}
public void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
Assert.notNull(failureHandler, "failureHandler cannot be null");
this.failureHandler = failureHandler;
}
}
@@ -1,439 +1,18 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* http://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;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.concurrent.SessionRegistry;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.SpringSecurityFilter;
import org.springframework.security.web.session.SessionUtils;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
/**
* Abstract processor of browser-based HTTP-based authentication requests.
*
* <h3>Authentication Process</h3>
*
* The filter requires that you set the <tt>authenticationManager</tt> property. An <tt>AuthenticationManager</tt> is
* required to process the authentication request tokens created by implementing classes.
* Renamed class, retained for backwards compatibility.
* <p>
* This filter will intercept a request and attempt to perform authentication from that request if
* the request URL matches the value of the <tt>filterProcessesUrl</tt> property. This behaviour can modified by
* overriding the method {@link #requiresAuthentication(HttpServletRequest, HttpServletResponse) requiresAuthentication}.
* <p>
* Authentication is performed by the {@link #attemptAuthentication(HttpServletRequest, HttpServletResponse)
* attemptAuthentication} method, which must be implemented by subclasses.
* See {@link AbstractAuthenticationProcessingFilter}.
*
* <h4>Authentication Success</h4>
*
* If authentication is successful, the resulting {@link Authentication} object will be placed into the
* <code>SecurityContext</code> for the current thread, which is guaranteed to have already been created by an earlier
* filter. The configured {@link #setAuthenticationSuccessHandler(AuthenticationSuccessHandler) AuthenticationSuccessHandler} will
* then be called to take the redirect to the appropriate destination after a successful login. The default behaviour
* is implemented in a {@link SavedRequestAwareAuthenticationSuccessHandler} which will make use of any
* <tt>SavedRequest</tt> set by the <tt>ExceptionTranslationFilter</tt> and redirect the user to the URL contained
* therein. Otherwise it will redirect to the webapp root "/". You can customize this behaviour by injecting a
* differently configured instance of this class, or by using a different implementation.
* <p>
* See the {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse, Authentication)
* successfulAuthentication} method for more information.
*
* <h4>Authentication Failure</h4>
*
* If authentication fails, the resulting <tt>AuthenticationException</tt> will be placed into the <tt>HttpSession</tt>
* with the attribute defined by {@link #SPRING_SECURITY_LAST_EXCEPTION_KEY}. It will then delegate to the configured
* {@link AuthenticationFailureHandler} to allow the failure information to be conveyed to the client.
* The default implementation is {@link SimpleUrlAuthenticationFailureHandler}, which sends a 401 error code to the
* client. It may also be configured with a failure URL as an alternative. Again you can inject whatever
* behaviour you require here.
*
* <h4>Event Publication</h4>
*
* If authentication is successful, an
* {@link org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent
* InteractiveAuthenticationSuccessEvent} will be published via the application context. No events will be published if
* authentication was unsuccessful, because this would generally be recorded via an
* <tt>AuthenticationManager</tt>-specific application event.
* <p>
* The filter has an optional attribute <tt>invalidateSessionOnSuccessfulAuthentication</tt> that will invalidate
* the current session on successful authentication. This is to protect against session fixation attacks (see
* <a href="http://en.wikipedia.org/wiki/Session_fixation">this Wikipedia article</a> for more information).
* The behaviour is turned off by default. Additionally there is a property <tt>migrateInvalidatedSessionAttributes</tt>
* which tells if on session invalidation we are to migrate all session attributes from the old session to a newly
* created one. This is turned on by default, but not used unless <tt>invalidateSessionOnSuccessfulAuthentication</tt>
* is true. If you are using this feature in combination with concurrent session control, you should set the
* <tt>sessionRegistry</tt> property to make sure that the session information is updated consistently.
*
* @author Ben Alex
* @author Luke Taylor
* @version $Id$
* @deprecated Use AbstractAuthenticationProcessingFilter instead.
*/
public abstract class AbstractProcessingFilter extends SpringSecurityFilter implements InitializingBean,
ApplicationEventPublisherAware, MessageSourceAware {
//~ Static fields/initializers =====================================================================================
@Deprecated
public abstract class AbstractProcessingFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_LAST_EXCEPTION_KEY = "SPRING_SECURITY_LAST_EXCEPTION";
//~ Instance fields ================================================================================================
protected ApplicationEventPublisher eventPublisher;
protected AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
private AuthenticationManager authenticationManager;
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
/*
* Delay use of NullRememberMeServices until initialization so that namespace has a chance to inject
* the RememberMeServices implementation into custom implementations.
*/
private RememberMeServices rememberMeServices = null;
/**
* The URL destination that this filter intercepts and processes (usually
* something like <code>/j_spring_security_check</code>)
*/
private String filterProcessesUrl;
private boolean continueChainBeforeSuccessfulAuthentication = false;
/**
* Tells if we on successful authentication should invalidate the
* current session. This is a common guard against session fixation attacks.
* Defaults to <code>false</code>.
*/
private boolean invalidateSessionOnSuccessfulAuthentication = false;
/**
* If {@link #invalidateSessionOnSuccessfulAuthentication} is true, this
* flag indicates that the session attributes of the session to be invalidated
* are to be migrated to the new session. Defaults to <code>true</code> since
* nothing will happen unless {@link #invalidateSessionOnSuccessfulAuthentication}
* is true.
*/
private boolean migrateInvalidatedSessionAttributes = true;
private boolean allowSessionCreation = true;
private SessionRegistry sessionRegistry;
private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
//~ Constructors ===================================================================================================
/**
* @param defaultFilterProcessesUrl the default value for <tt>filterProcessesUrl</tt>.
*/
protected AbstractProcessingFilter(String defaultFilterProcessesUrl) {
this.filterProcessesUrl = defaultFilterProcessesUrl;
}
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified");
Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL");
Assert.notNull(authenticationManager, "authenticationManager must be specified");
if (rememberMeServices == null) {
rememberMeServices = new NullRememberMeServices();
}
}
/**
* Invokes the {@link #requiresAuthentication(HttpServletRequest, HttpServletResponse) requiresAuthentication}
* method to determine whether the request is for authentication and should be handled by this filter.
* If it is an authentication request, the
* {@link #attemptAuthentication(HttpServletRequest, HttpServletResponse) attemptAuthentication} will be invoked
* to perform the authentication. There are then three possible outcomes:
* <ol>
* <li>An <tt>Authentication</tt> object is returned.
* The {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse, Authentication)
* successfulAuthentication} method will be invoked</li>
* <li>An <tt>AuthenticationException</tt> occurs during authentication.
* The {@link #unSuccessfulAuthentication(HttpServletRequest, HttpServletResponse, Authentication)
* unSuccessfulAuthentication} method will be invoked</li>
* <li>Null is returned, indicating that the authentication process is incomplete.
* The method will then return immediately, assuming that the subclass has done any necessary work (such as
* redirects) to continue the authentication process. The assumption is that a later request will be received
* by this method where the returned <tt>Authentication</tt> object is not null.
* </ol>
*/
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
try {
authResult = attemptAuthentication(request, response);
if (authResult == null) {
// return immediately as subclass has indicated that it hasn't completed authentication
return;
}
}
catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(request, response, failed);
return;
}
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, authResult);
}
/**
* Indicates whether this filter should attempt to process a login request for the current invocation.
* <p>
* It strips any parameters from the "path" section of the request URL (such
* as the jsessionid parameter in
* <em>http://host/myapp/index.html;jsessionid=blah</em>) before matching
* against the <code>filterProcessesUrl</code> property.
* <p>
* Subclasses may override for special requirements, such as Tapestry integration.
*
* @return <code>true</code> if the filter should attempt authentication, <code>false</code> otherwise.
*/
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
String uri = request.getRequestURI();
int pathParamIndex = uri.indexOf(';');
if (pathParamIndex > 0) {
// strip everything after the first semi-colon
uri = uri.substring(0, pathParamIndex);
}
if ("".equals(request.getContextPath())) {
return uri.endsWith(filterProcessesUrl);
}
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
}
/**
* Performs actual authentication.
* <p>
* The implementation should do one of the following:
* <ol>
* <li>Return a populated authentication token for the authenticated user, indicating successful authentication</li>
* <li>Return null, indicating that the authentication process is still in progress. Before returning, the
* implementation should perform any additional work required to complete the process.</li>
* <li>Throw an <tt>AuthenticationException</tt> if the authentication process fails</li>
* </ol>
*
* @param request from which to extract parameters and perform the authentication
* @param response the response, which may be needed if the implementation has to do a redirect as part of a
* multi-stage authentication process (such as OpenID).
*
* @return the authenticated user token, or null if authentication is incomplete.
*
* @throws AuthenticationException if authentication fails.
*/
public abstract Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException;
/**
* Default behaviour for successful authentication.
* <ol>
* <li>Sets the successful <tt>Authentication</tt> object on the {@link SecurityContextHolder}</li>
* <li>Performs any configured session migration behaviour</li>
* <li>Informs the configured <tt>RememberMeServices</tt> of the successful login</li>
* <li>Fires an {@link InteractiveAuthenticationSuccessEvent} via the configured
* <tt>ApplicationEventPublisher</tt></li>
* <li>Delegates additional behaviour to the {@link AuthenticationSuccessHandler}.</li>
* </ol>
*
* @param authResult the object returned from the <tt>attemptAuthentication</tt> method.
*/
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authResult) throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
if (invalidateSessionOnSuccessfulAuthentication) {
SessionUtils.startNewSessionIfRequired(request, migrateInvalidatedSessionAttributes, sessionRegistry);
}
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
successHandler.onAuthenticationSuccess(request, response, authResult);
}
/**
* Default behaviour for unsuccessful authentication.
* <ol>
* <li>Clears the {@link SecurityContextHolder}</li>
* <li>Stores the exception in the session (if it exists or <tt>allowSesssionCreation</tt> is set to <tt>true</tt>)</li>
* <li>Informs the configured <tt>RememberMeServices</tt> of the failed login</li>
* <li>Delegates additional behaviour to the {@link AuthenticationFailureHandler}.</li>
* </ol>
*/
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {
SecurityContextHolder.clearContext();
if (logger.isDebugEnabled()) {
logger.debug("Authentication request failed: " + failed.toString());
logger.debug("Updated SecurityContextHolder to contain null Authentication");
logger.debug("Delegating to authentication failure handler" + failureHandler);
}
try {
HttpSession session = request.getSession(false);
if (session != null || allowSessionCreation) {
request.getSession().setAttribute(SPRING_SECURITY_LAST_EXCEPTION_KEY, failed);
}
}
catch (Exception ignored) {
}
rememberMeServices.loginFail(request, response);
failureHandler.onAuthenticationFailure(request, response, failed);
}
protected AuthenticationManager getAuthenticationManager() {
return authenticationManager;
}
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
public String getFilterProcessesUrl() {
return filterProcessesUrl;
}
public void setFilterProcessesUrl(String filterProcessesUrl) {
this.filterProcessesUrl = filterProcessesUrl;
}
public RememberMeServices getRememberMeServices() {
return rememberMeServices;
}
public void setRememberMeServices(RememberMeServices rememberMeServices) {
this.rememberMeServices = rememberMeServices;
}
/**
* Indicates if the filter chain should be continued prior to delegation to
* {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse,
* Authentication)}, which may be useful in certain environment (such as
* Tapestry applications). Defaults to <code>false</code>.
*/
public void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {
this.continueChainBeforeSuccessfulAuthentication = continueChainBeforeSuccessfulAuthentication;
}
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
this.authenticationDetailsSource = authenticationDetailsSource;
}
public void setMessageSource(MessageSource messageSource) {
this.messages = new MessageSourceAccessor(messageSource);
}
public void setInvalidateSessionOnSuccessfulAuthentication(boolean invalidateSessionOnSuccessfulAuthentication) {
this.invalidateSessionOnSuccessfulAuthentication = invalidateSessionOnSuccessfulAuthentication;
}
public void setMigrateInvalidatedSessionAttributes(boolean migrateInvalidatedSessionAttributes) {
this.migrateInvalidatedSessionAttributes = migrateInvalidatedSessionAttributes;
}
public AuthenticationDetailsSource getAuthenticationDetailsSource() {
// Required due to SEC-310
return authenticationDetailsSource;
}
protected boolean getAllowSessionCreation() {
return allowSessionCreation;
}
public void setAllowSessionCreation(boolean allowSessionCreation) {
this.allowSessionCreation = allowSessionCreation;
}
/**
* The session registry needs to be set if session fixation attack protection is in use (and concurrent
* session control is enabled).
*/
public void setSessionRegistry(SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
}
/**
* Sets the strategy used to handle a successful authentication.
* By default a {@link SavedRequestAwareAuthenticationSuccessHandler} is used.
*/
public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler successHandler) {
Assert.notNull(successHandler, "successHandler cannot be null");
this.successHandler = successHandler;
}
public void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
Assert.notNull(failureHandler, "failureHandler cannot be null");
this.failureHandler = failureHandler;
super(defaultFilterProcessesUrl);
}
}
@@ -1,182 +1,15 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* http://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;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.FilterChainOrder;
import org.springframework.security.web.util.TextEscapeUtils;
import org.springframework.util.Assert;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Processes an authentication form.
* Renamed class, retained for backwards compatibility.
* <p>
* Login forms must present two parameters to this filter: a username and
* password. The default parameter names to use are contained in the
* static fields {@link #SPRING_SECURITY_FORM_USERNAME_KEY} and {@link #SPRING_SECURITY_FORM_PASSWORD_KEY}.
* The parameter names can also be changed by setting the <tt>usernameParameter</tt> and <tt>passwordParameter</tt>
* properties.
* <p>
* This filter by default responds to the URL <tt>/j_spring_security_check</tt>.
* See {@link UsernamePasswordAuthenticationProcessingFilter}.
*
* @author Ben Alex
* @author Colin Sampaleanu
* @author Luke Taylor
* @version $Id$
* @deprecated Use UsernamePasswordAuthenticationProcessingFilter instead.
*/
public class AuthenticationProcessingFilter extends AbstractProcessingFilter {
//~ Static fields/initializers =====================================================================================
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";
public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME";
public class AuthenticationProcessingFilter extends UsernamePasswordAuthenticationProcessingFilter {
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private boolean postOnly = true;
//~ Constructors ===================================================================================================
public AuthenticationProcessingFilter() {
super("/j_spring_security_check");
}
//~ Methods ========================================================================================================
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// Place the last username attempted into HttpSession for views
HttpSession session = request.getSession(false);
if (session != null || getAllowSessionCreation()) {
request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY, TextEscapeUtils.escapeEntities(username));
}
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
/**
* Enables subclasses to override the composition of the password, such as by including additional values
* and a separator.<p>This might be used for example if a postcode/zipcode was required in addition to the
* password. A delimiter such as a pipe (|) should be used to separate the password and extended value(s). The
* <code>AuthenticationDao</code> will need to generate the expected password in a corresponding manner.</p>
*
* @param request so that request attributes can be retrieved
*
* @return the password that will be presented in the <code>Authentication</code> request token to the
* <code>AuthenticationManager</code>
*/
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(passwordParameter);
}
/**
* Enables subclasses to override the composition of the username, such as by including additional values
* and a separator.
*
* @param request so that request attributes can be retrieved
*
* @return the username that will be presented in the <code>Authentication</code> request token to the
* <code>AuthenticationManager</code>
*/
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
/**
* Provided so that subclasses may configure what is put into the authentication request's details
* property.
*
* @param request that an authentication request is being created for
* @param authRequest the authentication request object that should have its details set
*/
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
/**
* Sets the parameter name which will be used to obtain the username from the login request.
*
* @param usernameParameter the parameter name. Defaults to "j_username".
*/
public void setUsernameParameter(String usernameParameter) {
Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
this.usernameParameter = usernameParameter;
}
/**
* Sets the parameter name which will be used to obtain the password from the login request..
*
* @param passwordParameter the parameter name. Defaults to "j_password".
*/
public void setPasswordParameter(String passwordParameter) {
Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
this.passwordParameter = passwordParameter;
}
/**
* Defines whether only HTTP POST requests will be allowed by this filter.
* If set to true, and an authentication request is received which is not a POST request, an exception will
* be raised immediately and authentication will not be attempted. The <tt>unsuccessfulAuthentication()</tt> method
* will be called as if handling a failed authentication.
* <p>
* Defaults to <tt>true</tt> but may be overridden by subclasses.
*/
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
public int getOrder() {
return FilterChainOrder.AUTHENTICATION_PROCESSING_FILTER;
}
public final String getUsernameParameter() {
return usernameParameter;
}
public final String getPasswordParameter() {
return passwordParameter;
}
}
@@ -1,258 +1,14 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* http://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;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.PortMapperImpl;
import org.springframework.security.web.PortResolver;
import org.springframework.security.web.PortResolverImpl;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.util.RedirectUrlBuilder;
import org.springframework.security.web.util.UrlUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Used by the {@link ExceptionTranslationFilter} to commence a form login
* authentication via the {@link AuthenticationProcessingFilter}. This object
* holds the location of the login form, relative to the web app context path,
* and is used to commence a redirect to that form.
* Renamed class, retained for backwards compatibility.
* <p>
* By setting the <em>forceHttps</em> property to true, you may configure the
* class to force the protocol used for the login form to be <code>HTTPS</code>,
* even if the original intercepted request for a resource used the
* <code>HTTP</code> protocol. When this happens, after a successful login
* (via HTTPS), the original resource will still be accessed as HTTP, via the
* original request URL. For the forced HTTPS feature to work, the {@link
* PortMapper} is consulted to determine the HTTP:HTTPS pairs.
* See {@link LoginUrlAuthenticationEntryPoint}.
*
* @author Ben Alex
* @author colin sampaleanu
* @author Omri Spector
* @author Luke Taylor
* @version $Id$
* @deprecated Use LoginUrlAuthenticationEntryPoint instead.
*/
public class AuthenticationProcessingFilterEntryPoint implements AuthenticationEntryPoint, InitializingBean {
//~ Static fields/initializers =====================================================================================
public class AuthenticationProcessingFilterEntryPoint extends LoginUrlAuthenticationEntryPoint{
private static final Log logger = LogFactory.getLog(AuthenticationProcessingFilterEntryPoint.class);
//~ Instance fields ================================================================================================
private PortMapper portMapper = new PortMapperImpl();
private PortResolver portResolver = new PortResolverImpl();
private String loginFormUrl;
private boolean forceHttps = false;
private boolean useForward = false;
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.isTrue(StringUtils.hasText(loginFormUrl) && UrlUtils.isValidRedirectUrl(loginFormUrl),
"loginFormUrl must be specified and must be a valid redirect URL");
Assert.notNull(portMapper, "portMapper must be specified");
Assert.notNull(portResolver, "portResolver must be specified");
}
/**
* Allows subclasses to modify the login form URL that should be applicable for a given request.
*
* @param request the request
* @param response the response
* @param exception the exception
* @return the URL (cannot be null or empty; defaults to {@link #getLoginFormUrl()})
*/
protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) {
return getLoginFormUrl();
}
/**
* Performs the redirect (or forward) to the login form URL.
*/
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String redirectUrl = null;
if (useForward) {
if (forceHttps && "http".equals(request.getScheme())) {
redirectUrl = buildHttpsRedirectUrlForRequest(httpRequest);
}
if (redirectUrl == null) {
String loginForm = determineUrlToUseForThisRequest(httpRequest, httpResponse, authException);
if (logger.isDebugEnabled()) {
logger.debug("Server side forward to: " + loginForm);
}
RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(loginForm);
dispatcher.forward(request, response);
return;
}
} else {
// redirect to login page. Use https if forceHttps true
redirectUrl = buildRedirectUrlToLoginPage(httpRequest, httpResponse, authException);
}
httpResponse.sendRedirect(httpResponse.encodeRedirectURL(redirectUrl));
}
protected String buildRedirectUrlToLoginPage(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) {
String loginForm = determineUrlToUseForThisRequest(request, response, authException);
int serverPort = portResolver.getServerPort(request);
String scheme = request.getScheme();
RedirectUrlBuilder urlBuilder = new RedirectUrlBuilder();
urlBuilder.setScheme(scheme);
urlBuilder.setServerName(request.getServerName());
urlBuilder.setPort(serverPort);
urlBuilder.setContextPath(request.getContextPath());
urlBuilder.setPathInfo(loginForm);
if (forceHttps && "http".equals(scheme)) {
Integer httpsPort = portMapper.lookupHttpsPort(new Integer(serverPort));
if (httpsPort != null) {
// Overwrite scheme and port in the redirect URL
urlBuilder.setScheme("https");
urlBuilder.setPort(httpsPort.intValue());
} else {
logger.warn("Unable to redirect to HTTPS as no port mapping found for HTTP port " + serverPort);
}
}
return urlBuilder.getUrl();
}
/**
* Builds a URL to redirect the supplied request to HTTPS.
*/
protected String buildHttpsRedirectUrlForRequest(HttpServletRequest request)
throws IOException, ServletException {
int serverPort = portResolver.getServerPort(request);
Integer httpsPort = portMapper.lookupHttpsPort(new Integer(serverPort));
if (httpsPort != null) {
RedirectUrlBuilder urlBuilder = new RedirectUrlBuilder();
urlBuilder.setScheme("https");
urlBuilder.setServerName(request.getServerName());
urlBuilder.setPort(httpsPort.intValue());
urlBuilder.setContextPath(request.getContextPath());
urlBuilder.setServletPath(request.getServletPath());
urlBuilder.setPathInfo(request.getPathInfo());
urlBuilder.setQuery(request.getQueryString());
return urlBuilder.getUrl();
}
// Fall through to server-side forward with warning message
logger.warn("Unable to redirect to HTTPS as no port mapping found for HTTP port " + serverPort);
return null;
}
/**
* Set to true to force login form access to be via https. If this value is true (the default is false),
* and the incoming request for the protected resource which triggered the interceptor was not already
* <code>https</code>, then the client will first be redirected to an https URL, even if <tt>serverSideRedirect</tt>
* is set to <tt>true</tt>.
*/
public void setForceHttps(boolean forceHttps) {
this.forceHttps = forceHttps;
}
protected boolean isForceHttps() {
return forceHttps;
}
/**
* The URL where the <code>AuthenticationProcessingFilter</code> login
* page can be found. Should be relative to the web-app context path, and
* include a leading <code>/</code>
*/
public void setLoginFormUrl(String loginFormUrl) {
this.loginFormUrl = loginFormUrl;
}
public String getLoginFormUrl() {
return loginFormUrl;
}
public void setPortMapper(PortMapper portMapper) {
this.portMapper = portMapper;
}
protected PortMapper getPortMapper() {
return portMapper;
}
public void setPortResolver(PortResolver portResolver) {
this.portResolver = portResolver;
}
protected PortResolver getPortResolver() {
return portResolver;
}
/**
* Tells if we are to do a forward to the <code>loginFormUrl</code> using the <tt>RequestDispatcher</tt>,
* instead of a 302 redirect.
*
* @param useForward
*/
public void setUseForward(boolean useForward) {
this.useForward = useForward;
}
protected boolean isUseForward() {
return useForward;
}
}
@@ -14,7 +14,7 @@ import org.springframework.security.core.Authentication;
* Implementations can do whatever they want but typical behaviour would be to control the navigation to the
* subsequent destination (using a redirect or a forward). For example, after a user has logged in by submitting a
* login form, the application needs to decide where they should be redirected to afterwards
* (see {@link AbstractProcessingFilter} and subclasses). Other logic may also be included if required.
* (see {@link AbstractAuthenticationProcessingFilter} and subclasses). Other logic may also be included if required.
*
* @author Luke Taylor
* @version $Id$
@@ -0,0 +1,62 @@
package org.springframework.security.web.authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.Ordered;
/**
* <p>
* In the pre-authenticated authentication case (unlike CAS, for example) the
* user will already have been identified through some external mechanism and a
* secure context established by the time the security-enforcement filter is
* invoked.
* <p>
* Therefore this class isn't actually responsible for the commencement of
* authentication, as it is in the case of other providers. It will be called if
* the user is rejected by the AbstractPreAuthenticatedProcessingFilter,
* resulting in a null authentication.
* <p>
* The <code>commence</code> method will always return an
* <code>HttpServletResponse.SC_FORBIDDEN</code> (403 error).
*
* @see org.springframework.security.web.access.ExceptionTranslationFilter
*
* @author Luke Taylor
* @author Ruud Senden
* @since 2.0
*/
public class Http403ForbiddenEntryPoint implements AuthenticationEntryPoint, Ordered {
private static final Log logger = LogFactory.getLog(Http403ForbiddenEntryPoint.class);
private int order = Integer.MAX_VALUE;
/**
* Always returns a 403 error code to the client.
*/
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2) throws IOException,
ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Pre-authenticated entry point called. Rejecting access");
}
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
}
public int getOrder() {
return order;
}
public void setOrder(int i) {
order = i;
}
}
@@ -0,0 +1,260 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* http://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;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.PortMapper;
import org.springframework.security.web.PortMapperImpl;
import org.springframework.security.web.PortResolver;
import org.springframework.security.web.PortResolverImpl;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.util.RedirectUrlBuilder;
import org.springframework.security.web.util.UrlUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Used by the {@link ExceptionTranslationFilter} to commence a form login
* authentication via the {@link UsernamePasswordAuthenticationProcessingFilter}. This object
* holds the location of the login form, relative to the web app context path,
* and is used to commence a redirect to that form.
* <p>
* By setting the <em>forceHttps</em> property to true, you may configure the
* class to force the protocol used for the login form to be <code>HTTPS</code>,
* even if the original intercepted request for a resource used the
* <code>HTTP</code> protocol. When this happens, after a successful login
* (via HTTPS), the original resource will still be accessed as HTTP, via the
* original request URL. For the forced HTTPS feature to work, the {@link
* PortMapper} is consulted to determine the HTTP:HTTPS pairs.
*
* @author Ben Alex
* @author colin sampaleanu
* @author Omri Spector
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public class LoginUrlAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {
//~ Static fields/initializers =====================================================================================
private static final Log logger = LogFactory.getLog(LoginUrlAuthenticationEntryPoint.class);
//~ Instance fields ================================================================================================
private PortMapper portMapper = new PortMapperImpl();
private PortResolver portResolver = new PortResolverImpl();
private String loginFormUrl;
private boolean forceHttps = false;
private boolean useForward = false;
//~ Methods ========================================================================================================
public void afterPropertiesSet() throws Exception {
Assert.isTrue(StringUtils.hasText(loginFormUrl) && UrlUtils.isValidRedirectUrl(loginFormUrl),
"loginFormUrl must be specified and must be a valid redirect URL");
Assert.notNull(portMapper, "portMapper must be specified");
Assert.notNull(portResolver, "portResolver must be specified");
}
/**
* Allows subclasses to modify the login form URL that should be applicable for a given request.
*
* @param request the request
* @param response the response
* @param exception the exception
* @return the URL (cannot be null or empty; defaults to {@link #getLoginFormUrl()})
*/
protected String determineUrlToUseForThisRequest(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) {
return getLoginFormUrl();
}
/**
* Performs the redirect (or forward) to the login form URL.
*/
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String redirectUrl = null;
if (useForward) {
if (forceHttps && "http".equals(request.getScheme())) {
redirectUrl = buildHttpsRedirectUrlForRequest(httpRequest);
}
if (redirectUrl == null) {
String loginForm = determineUrlToUseForThisRequest(httpRequest, httpResponse, authException);
if (logger.isDebugEnabled()) {
logger.debug("Server side forward to: " + loginForm);
}
RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(loginForm);
dispatcher.forward(request, response);
return;
}
} else {
// redirect to login page. Use https if forceHttps true
redirectUrl = buildRedirectUrlToLoginPage(httpRequest, httpResponse, authException);
}
httpResponse.sendRedirect(httpResponse.encodeRedirectURL(redirectUrl));
}
protected String buildRedirectUrlToLoginPage(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) {
String loginForm = determineUrlToUseForThisRequest(request, response, authException);
int serverPort = portResolver.getServerPort(request);
String scheme = request.getScheme();
RedirectUrlBuilder urlBuilder = new RedirectUrlBuilder();
urlBuilder.setScheme(scheme);
urlBuilder.setServerName(request.getServerName());
urlBuilder.setPort(serverPort);
urlBuilder.setContextPath(request.getContextPath());
urlBuilder.setPathInfo(loginForm);
if (forceHttps && "http".equals(scheme)) {
Integer httpsPort = portMapper.lookupHttpsPort(new Integer(serverPort));
if (httpsPort != null) {
// Overwrite scheme and port in the redirect URL
urlBuilder.setScheme("https");
urlBuilder.setPort(httpsPort.intValue());
} else {
logger.warn("Unable to redirect to HTTPS as no port mapping found for HTTP port " + serverPort);
}
}
return urlBuilder.getUrl();
}
/**
* Builds a URL to redirect the supplied request to HTTPS.
*/
protected String buildHttpsRedirectUrlForRequest(HttpServletRequest request)
throws IOException, ServletException {
int serverPort = portResolver.getServerPort(request);
Integer httpsPort = portMapper.lookupHttpsPort(new Integer(serverPort));
if (httpsPort != null) {
RedirectUrlBuilder urlBuilder = new RedirectUrlBuilder();
urlBuilder.setScheme("https");
urlBuilder.setServerName(request.getServerName());
urlBuilder.setPort(httpsPort.intValue());
urlBuilder.setContextPath(request.getContextPath());
urlBuilder.setServletPath(request.getServletPath());
urlBuilder.setPathInfo(request.getPathInfo());
urlBuilder.setQuery(request.getQueryString());
return urlBuilder.getUrl();
}
// Fall through to server-side forward with warning message
logger.warn("Unable to redirect to HTTPS as no port mapping found for HTTP port " + serverPort);
return null;
}
/**
* Set to true to force login form access to be via https. If this value is true (the default is false),
* and the incoming request for the protected resource which triggered the interceptor was not already
* <code>https</code>, then the client will first be redirected to an https URL, even if <tt>serverSideRedirect</tt>
* is set to <tt>true</tt>.
*/
public void setForceHttps(boolean forceHttps) {
this.forceHttps = forceHttps;
}
protected boolean isForceHttps() {
return forceHttps;
}
/**
* The URL where the <code>UsernamePasswordAuthenticationProcessingFilter</code> login
* page can be found. Should be relative to the web-app context path, and
* include a leading <code>/</code>
*/
public void setLoginFormUrl(String loginFormUrl) {
this.loginFormUrl = loginFormUrl;
}
public String getLoginFormUrl() {
return loginFormUrl;
}
public void setPortMapper(PortMapper portMapper) {
this.portMapper = portMapper;
}
protected PortMapper getPortMapper() {
return portMapper;
}
public void setPortResolver(PortResolver portResolver) {
this.portResolver = portResolver;
}
protected PortResolver getPortResolver() {
return portResolver;
}
/**
* Tells if we are to do a forward to the <code>loginFormUrl</code> using the <tt>RequestDispatcher</tt>,
* instead of a 302 redirect.
*
* @param useForward
*/
public void setUseForward(boolean useForward) {
this.useForward = useForward;
}
protected boolean isUseForward() {
return useForward;
}
}
@@ -25,7 +25,7 @@ import org.springframework.security.core.Authentication;
* Implement by a class that is capable of providing a remember-me service.
*
* <p>
* Spring Security filters (namely {@link org.springframework.security.web.authentication.AbstractProcessingFilter} and
* Spring Security filters (namely {@link org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter} and
* {@link RememberMeProcessingFilter} will call the methods provided by an implementation of this interface.
* <p>
* Implementations may implement any type of remember-me capability they wish.
@@ -0,0 +1,185 @@
/* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* 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
*
* http://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;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.FilterChainOrder;
import org.springframework.security.web.util.TextEscapeUtils;
import org.springframework.util.Assert;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Processes an authentication form. Called <tt>AuthenticationProcessingFilter<tt> in previous versions
* of the framework.
* <p>
* Login forms must present two parameters to this filter: a username and
* password. The default parameter names to use are contained in the
* static fields {@link #SPRING_SECURITY_FORM_USERNAME_KEY} and {@link #SPRING_SECURITY_FORM_PASSWORD_KEY}.
* The parameter names can also be changed by setting the <tt>usernameParameter</tt> and <tt>passwordParameter</tt>
* properties.
* <p>
* This filter by default responds to the URL <tt>/j_spring_security_check</tt>.
*
* @author Ben Alex
* @author Colin Sampaleanu
* @author Luke Taylor
* @version $Id$
* @since 3.0
*/
public class UsernamePasswordAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
//~ Static fields/initializers =====================================================================================
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";
public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private boolean postOnly = true;
//~ Constructors ===================================================================================================
public UsernamePasswordAuthenticationProcessingFilter() {
super("/j_spring_security_check");
}
//~ Methods ========================================================================================================
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = "";
}
if (password == null) {
password = "";
}
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
// Place the last username attempted into HttpSession for views
HttpSession session = request.getSession(false);
if (session != null || getAllowSessionCreation()) {
request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY, TextEscapeUtils.escapeEntities(username));
}
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
/**
* Enables subclasses to override the composition of the password, such as by including additional values
* and a separator.<p>This might be used for example if a postcode/zipcode was required in addition to the
* password. A delimiter such as a pipe (|) should be used to separate the password and extended value(s). The
* <code>AuthenticationDao</code> will need to generate the expected password in a corresponding manner.</p>
*
* @param request so that request attributes can be retrieved
*
* @return the password that will be presented in the <code>Authentication</code> request token to the
* <code>AuthenticationManager</code>
*/
protected String obtainPassword(HttpServletRequest request) {
return request.getParameter(passwordParameter);
}
/**
* Enables subclasses to override the composition of the username, such as by including additional values
* and a separator.
*
* @param request so that request attributes can be retrieved
*
* @return the username that will be presented in the <code>Authentication</code> request token to the
* <code>AuthenticationManager</code>
*/
protected String obtainUsername(HttpServletRequest request) {
return request.getParameter(usernameParameter);
}
/**
* Provided so that subclasses may configure what is put into the authentication request's details
* property.
*
* @param request that an authentication request is being created for
* @param authRequest the authentication request object that should have its details set
*/
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
}
/**
* Sets the parameter name which will be used to obtain the username from the login request.
*
* @param usernameParameter the parameter name. Defaults to "j_username".
*/
public void setUsernameParameter(String usernameParameter) {
Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
this.usernameParameter = usernameParameter;
}
/**
* Sets the parameter name which will be used to obtain the password from the login request..
*
* @param passwordParameter the parameter name. Defaults to "j_password".
*/
public void setPasswordParameter(String passwordParameter) {
Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
this.passwordParameter = passwordParameter;
}
/**
* Defines whether only HTTP POST requests will be allowed by this filter.
* If set to true, and an authentication request is received which is not a POST request, an exception will
* be raised immediately and authentication will not be attempted. The <tt>unsuccessfulAuthentication()</tt> method
* will be called as if handling a failed authentication.
* <p>
* Defaults to <tt>true</tt> but may be overridden by subclasses.
*/
public void setPostOnly(boolean postOnly) {
this.postOnly = postOnly;
}
public int getOrder() {
return FilterChainOrder.AUTHENTICATION_PROCESSING_FILTER;
}
public final String getUsernameParameter() {
return usernameParameter;
}
public final String getPasswordParameter() {
return passwordParameter;
}
}
@@ -8,7 +8,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.web.SpringSecurityFilter;
import org.springframework.security.web.authentication.AbstractProcessingFilter;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
@@ -128,7 +128,7 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends SpringSec
if (logger.isDebugEnabled()) {
logger.debug("Cleared security context due to exception", failed);
}
request.getSession().setAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY, failed);
request.getSession().setAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY, failed);
}
/**
@@ -1,65 +1,16 @@
package org.springframework.security.web.authentication.preauth;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.Ordered;
/**
* <p>
* In the pre-authenticated authentication case (unlike CAS, for example) the
* user will already have been identified through some external mechanism and a
* secure context established by the time the security-enforcement filter is
* invoked.
* <p>
* Therefore this class isn't actually responsible for the commencement of
* authentication, as it is in the case of other providers. It will be called if
* the user is rejected by the AbstractPreAuthenticatedProcessingFilter,
* resulting in a null authentication.
* <p>
* The <code>commence</code> method will always return an
* <code>HttpServletResponse.SC_FORBIDDEN</code> (403 error).
* <p>
* This code is based on
* {@link org.springframework.security.ui.x509.X509ProcessingFilterEntryPoint}.
*
* @see org.springframework.security.web.access.ExceptionTranslationFilter
*
* @author Luke Taylor
* @author Ruud Senden
* @since 2.0
*/
public class PreAuthenticatedProcessingFilterEntryPoint implements AuthenticationEntryPoint, Ordered {
private static final Log logger = LogFactory.getLog(PreAuthenticatedProcessingFilterEntryPoint.class);
private int order = Integer.MAX_VALUE;
/**
* Always returns a 403 error code to the client.
*/
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException arg2) throws IOException,
ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Pre-authenticated entry point called. Rejecting access");
}
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
}
public int getOrder() {
return order;
}
public void setOrder(int i) {
order = i;
}
}
package org.springframework.security.web.authentication.preauth;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
/**
* Renamed class, retained for backwards compatibility.
* <p>
* See {@link Http403ForbiddenEntryPoint}.
*
* @author Luke Taylor
* @version $Id$
* @deprecated Use Http403ForbiddenEntryPoint instead.
*/
public class PreAuthenticatedProcessingFilterEntryPoint extends Http403ForbiddenEntryPoint {
}
@@ -12,8 +12,8 @@ import org.springframework.beans.BeanWrapperImpl;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.FilterChainOrder;
import org.springframework.security.web.SpringSecurityFilter;
import org.springframework.security.web.authentication.AbstractProcessingFilter;
import org.springframework.security.web.authentication.AuthenticationProcessingFilter;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
/**
@@ -39,19 +39,19 @@ public class DefaultLoginPageGeneratingFilter extends SpringSecurityFilter {
private String openIDusernameParameter;
private String openIDrememberMeParameter;
public DefaultLoginPageGeneratingFilter(AbstractProcessingFilter filter) {
if (filter instanceof AuthenticationProcessingFilter) {
init((AuthenticationProcessingFilter)filter, null);
public DefaultLoginPageGeneratingFilter(AbstractAuthenticationProcessingFilter filter) {
if (filter instanceof UsernamePasswordAuthenticationProcessingFilter) {
init((UsernamePasswordAuthenticationProcessingFilter)filter, null);
} else {
init(null, filter);
}
}
public DefaultLoginPageGeneratingFilter(AuthenticationProcessingFilter authFilter, AbstractProcessingFilter openIDFilter) {
public DefaultLoginPageGeneratingFilter(UsernamePasswordAuthenticationProcessingFilter authFilter, AbstractAuthenticationProcessingFilter openIDFilter) {
init(authFilter, openIDFilter);
}
private void init(AuthenticationProcessingFilter authFilter, AbstractProcessingFilter openIDFilter) {
private void init(UsernamePasswordAuthenticationProcessingFilter authFilter, AbstractAuthenticationProcessingFilter openIDFilter) {
if (authFilter != null) {
formLoginEnabled = true;
authenticationUrl = authFilter.getFilterProcessesUrl();
@@ -96,8 +96,8 @@ public class DefaultLoginPageGeneratingFilter extends SpringSecurityFilter {
HttpSession session = request.getSession(false);
if(session != null) {
lastUser = (String) session.getAttribute(AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY);
AuthenticationException ex = (AuthenticationException) session.getAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY);
lastUser = (String) session.getAttribute(UsernamePasswordAuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY);
AuthenticationException ex = (AuthenticationException) session.getAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY);
errorMsg = ex != null ? ex.getMessage() : "none";
if (lastUser == null) {
lastUser = "";
@@ -35,7 +35,7 @@ import java.util.TreeMap;
/**
* Represents central information from a <code>HttpServletRequest</code>.<p>This class is used by {@link
* org.springframework.security.web.authentication.AbstractProcessingFilter} and {@link org.springframework.security.web.wrapper.SavedRequestAwareWrapper} to
* org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter} and {@link org.springframework.security.web.wrapper.SavedRequestAwareWrapper} to
* reproduce the request after successful authentication. An instance of this class is stored at the time of an
* authentication exception by {@link org.springframework.security.web.access.ExceptionTranslationFilter}.</p>
* <p><em>IMPLEMENTATION NOTE</em>: It is assumed that this object is accessed only from the context of a single
@@ -1,6 +1,6 @@
package org.springframework.security.web.util;
import org.springframework.security.web.authentication.AbstractProcessingFilter;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import javax.servlet.http.HttpServletRequest;
@@ -20,7 +20,7 @@ public abstract class RedirectUtils {
//~ Methods ========================================================================================================
/**
* Encapsulates the redirect logic used in classes like {@link AbstractProcessingFilter} and {@link LogoutFilter}.
* Encapsulates the redirect logic used in classes like {@link AbstractAuthenticationProcessingFilter} and {@link LogoutFilter}.
*
* @param request the incoming request
* @param response the response to redirect
@@ -28,7 +28,7 @@ public abstract class RedirectUtils {
* @param useRelativeContext if true, causes any redirection URLs to be calculated minus the protocol
* and context path.
*
* @see AbstractProcessingFilter#setUseRelativeContext(boolean)
* @see AbstractAuthenticationProcessingFilter#setUseRelativeContext(boolean)
*/
public static final void sendRedirect(HttpServletRequest request,
HttpServletResponse response,
@@ -44,7 +44,7 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.PortResolverImpl;
import org.springframework.security.web.authentication.AbstractProcessingFilter;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
@@ -53,7 +53,7 @@ import org.springframework.security.web.savedrequest.SavedRequest;
/**
* Tests {@link AbstractProcessingFilter}.
* Tests {@link AbstractAuthenticationProcessingFilter}.
*
* @author Ben Alex
* @version $Id$
@@ -191,7 +191,7 @@ public class AbstractProcessingFilterTests extends TestCase {
}
public void testGettersSetters() throws Exception {
AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
AbstractAuthenticationProcessingFilter filter = new MockAbstractProcessingFilter();
filter.setAuthenticationManager(mock(AuthenticationManager.class));
filter.setFilterProcessesUrl("/p");
filter.afterPropertiesSet();
@@ -254,7 +254,7 @@ public class AbstractProcessingFilterTests extends TestCase {
}
public void testStartupDetectsInvalidAuthenticationManager() throws Exception {
AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
AbstractAuthenticationProcessingFilter filter = new MockAbstractProcessingFilter();
filter.setAuthenticationFailureHandler(failureHandler);
successHandler.setDefaultTargetUrl("/");
filter.setAuthenticationSuccessHandler(successHandler);
@@ -269,7 +269,7 @@ public class AbstractProcessingFilterTests extends TestCase {
}
public void testStartupDetectsInvalidFilterProcessesUrl() throws Exception {
AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
AbstractAuthenticationProcessingFilter filter = new MockAbstractProcessingFilter();
filter.setAuthenticationFailureHandler(failureHandler);
filter.setAuthenticationManager(mock(AuthenticationManager.class));
filter.setAuthenticationSuccessHandler(successHandler);
@@ -520,7 +520,7 @@ public class AbstractProcessingFilterTests extends TestCase {
//~ Inner Classes ==================================================================================================
private class MockAbstractProcessingFilter extends AbstractProcessingFilter {
private class MockAbstractProcessingFilter extends AbstractAuthenticationProcessingFilter {
private AuthenticationException exceptionToThrow;
private boolean grantAccess;
@@ -20,7 +20,7 @@ import junit.framework.TestCase;
import org.springframework.security.MockPortResolver;
import org.springframework.security.web.PortMapperImpl;
import org.springframework.security.web.authentication.AuthenticationProcessingFilterEntryPoint;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
@@ -30,7 +30,7 @@ import java.util.Map;
/**
* Tests {@link AuthenticationProcessingFilterEntryPoint}.
* Tests {@link LoginUrlAuthenticationEntryPoint}.
*
* @author Ben Alex
* @author colin sampaleanu
@@ -40,7 +40,7 @@ public class AuthenticationProcessingFilterEntryPointTests extends TestCase {
//~ Methods ========================================================================================================
public void testDetectsMissingLoginFormUrl() throws Exception {
AuthenticationProcessingFilterEntryPoint ep = new AuthenticationProcessingFilterEntryPoint();
LoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint();
ep.setPortMapper(new PortMapperImpl());
ep.setPortResolver(new MockPortResolver(80, 443));
@@ -52,7 +52,7 @@ public class AuthenticationProcessingFilterEntryPointTests extends TestCase {
}
public void testDetectsMissingPortMapper() throws Exception {
AuthenticationProcessingFilterEntryPoint ep = new AuthenticationProcessingFilterEntryPoint();
LoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint();
ep.setLoginFormUrl("xxx");
ep.setPortMapper(null);
@@ -64,7 +64,7 @@ public class AuthenticationProcessingFilterEntryPointTests extends TestCase {
}
public void testDetectsMissingPortResolver() throws Exception {
AuthenticationProcessingFilterEntryPoint ep = new AuthenticationProcessingFilterEntryPoint();
LoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint();
ep.setLoginFormUrl("xxx");
ep.setPortResolver(null);
@@ -76,7 +76,7 @@ public class AuthenticationProcessingFilterEntryPointTests extends TestCase {
}
public void testGettersSetters() {
AuthenticationProcessingFilterEntryPoint ep = new AuthenticationProcessingFilterEntryPoint();
LoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint();
ep.setLoginFormUrl("/hello");
ep.setPortMapper(new PortMapperImpl());
ep.setPortResolver(new MockPortResolver(8080, 8443));
@@ -100,7 +100,7 @@ public class AuthenticationProcessingFilterEntryPointTests extends TestCase {
MockHttpServletResponse response = new MockHttpServletResponse();
AuthenticationProcessingFilterEntryPoint ep = new AuthenticationProcessingFilterEntryPoint();
LoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint();
ep.setLoginFormUrl("/hello");
ep.setPortMapper(new PortMapperImpl());
ep.setForceHttps(true);
@@ -129,7 +129,7 @@ public class AuthenticationProcessingFilterEntryPointTests extends TestCase {
portMapper.setPortMappings(map);
response = new MockHttpServletResponse();
ep = new AuthenticationProcessingFilterEntryPoint();
ep = new LoginUrlAuthenticationEntryPoint();
ep.setLoginFormUrl("/hello");
ep.setPortMapper(new PortMapperImpl());
ep.setForceHttps(true);
@@ -151,7 +151,7 @@ public class AuthenticationProcessingFilterEntryPointTests extends TestCase {
MockHttpServletResponse response = new MockHttpServletResponse();
AuthenticationProcessingFilterEntryPoint ep = new AuthenticationProcessingFilterEntryPoint();
LoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint();
ep.setLoginFormUrl("/hello");
ep.setPortMapper(new PortMapperImpl());
ep.setForceHttps(true);
@@ -170,7 +170,7 @@ public class AuthenticationProcessingFilterEntryPointTests extends TestCase {
}
public void testNormalOperation() throws Exception {
AuthenticationProcessingFilterEntryPoint ep = new AuthenticationProcessingFilterEntryPoint();
LoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint();
ep.setLoginFormUrl("/hello");
ep.setPortMapper(new PortMapperImpl());
ep.setPortResolver(new MockPortResolver(80, 443));
@@ -191,7 +191,7 @@ public class AuthenticationProcessingFilterEntryPointTests extends TestCase {
}
public void testOperationWhenHttpsRequestsButHttpsPortUnknown() throws Exception {
AuthenticationProcessingFilterEntryPoint ep = new AuthenticationProcessingFilterEntryPoint();
LoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint();
ep.setLoginFormUrl("/hello");
ep.setPortResolver(new MockPortResolver(8888, 1234));
ep.setForceHttps(true);
@@ -214,7 +214,7 @@ public class AuthenticationProcessingFilterEntryPointTests extends TestCase {
}
public void testServerSideRedirectWithoutForceHttpsForwardsToLoginPage() throws Exception {
AuthenticationProcessingFilterEntryPoint ep = new AuthenticationProcessingFilterEntryPoint();
LoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint();
ep.setLoginFormUrl("/hello");
ep.setUseForward(true);
ep.afterPropertiesSet();
@@ -234,7 +234,7 @@ public class AuthenticationProcessingFilterEntryPointTests extends TestCase {
}
public void testServerSideRedirectWithForceHttpsRedirectsCurrentRequest() throws Exception {
AuthenticationProcessingFilterEntryPoint ep = new AuthenticationProcessingFilterEntryPoint();
LoginUrlAuthenticationEntryPoint ep = new LoginUrlAuthenticationEntryPoint();
ep.setLoginFormUrl("/hello");
ep.setUseForward(true);
ep.setForceHttps(true);
@@ -35,7 +35,7 @@ import org.springframework.security.core.AuthenticationException;
/**
* Tests {@link AuthenticationProcessingFilter}.
* Tests {@link UsernamePasswordAuthenticationProcessingFilter}.
*
* @author Ben Alex
* @version $Id$
@@ -46,10 +46,10 @@ public class AuthenticationProcessingFilterTests extends TestCase {
@Test
public void testNormalOperation() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/");
request.addParameter(AuthenticationProcessingFilter.SPRING_SECURITY_FORM_USERNAME_KEY, "rod");
request.addParameter(AuthenticationProcessingFilter.SPRING_SECURITY_FORM_PASSWORD_KEY, "koala");
request.addParameter(UsernamePasswordAuthenticationProcessingFilter.SPRING_SECURITY_FORM_USERNAME_KEY, "rod");
request.addParameter(UsernamePasswordAuthenticationProcessingFilter.SPRING_SECURITY_FORM_PASSWORD_KEY, "koala");
AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
UsernamePasswordAuthenticationProcessingFilter filter = new UsernamePasswordAuthenticationProcessingFilter();
assertEquals("/j_spring_security_check", filter.getFilterProcessesUrl());
filter.setAuthenticationManager(createAuthenticationManager());
filter.init(null);
@@ -57,16 +57,16 @@ public class AuthenticationProcessingFilterTests extends TestCase {
Authentication result = filter.attemptAuthentication(request, new MockHttpServletResponse());
assertTrue(result != null);
assertEquals("rod", request.getSession().getAttribute(
AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY));
UsernamePasswordAuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY));
assertEquals("127.0.0.1", ((WebAuthenticationDetails) result.getDetails()).getRemoteAddress());
}
@Test
public void testNullPasswordHandledGracefully() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/");
request.addParameter(AuthenticationProcessingFilter.SPRING_SECURITY_FORM_USERNAME_KEY, "rod");
request.addParameter(UsernamePasswordAuthenticationProcessingFilter.SPRING_SECURITY_FORM_USERNAME_KEY, "rod");
AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
UsernamePasswordAuthenticationProcessingFilter filter = new UsernamePasswordAuthenticationProcessingFilter();
filter.setAuthenticationManager(createAuthenticationManager());
assertNotNull(filter.attemptAuthentication(request, new MockHttpServletResponse()));
}
@@ -74,16 +74,16 @@ public class AuthenticationProcessingFilterTests extends TestCase {
@Test
public void testNullUsernameHandledGracefully() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/");
request.addParameter(AuthenticationProcessingFilter.SPRING_SECURITY_FORM_PASSWORD_KEY, "koala");
request.addParameter(UsernamePasswordAuthenticationProcessingFilter.SPRING_SECURITY_FORM_PASSWORD_KEY, "koala");
AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
UsernamePasswordAuthenticationProcessingFilter filter = new UsernamePasswordAuthenticationProcessingFilter();
filter.setAuthenticationManager(createAuthenticationManager());
assertNotNull(filter.attemptAuthentication(request, new MockHttpServletResponse()));
}
@Test
public void testUsingDifferentParameterNamesWorksAsExpected() throws ServletException {
AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
UsernamePasswordAuthenticationProcessingFilter filter = new UsernamePasswordAuthenticationProcessingFilter();
filter.setAuthenticationManager(createAuthenticationManager());
filter.setUsernameParameter("x");
filter.setPasswordParameter("y");
@@ -100,10 +100,10 @@ public class AuthenticationProcessingFilterTests extends TestCase {
@Test
public void testSpacesAreTrimmedCorrectlyFromUsername() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/");
request.addParameter(AuthenticationProcessingFilter.SPRING_SECURITY_FORM_USERNAME_KEY, " rod ");
request.addParameter(AuthenticationProcessingFilter.SPRING_SECURITY_FORM_PASSWORD_KEY, "koala");
request.addParameter(UsernamePasswordAuthenticationProcessingFilter.SPRING_SECURITY_FORM_USERNAME_KEY, " rod ");
request.addParameter(UsernamePasswordAuthenticationProcessingFilter.SPRING_SECURITY_FORM_PASSWORD_KEY, "koala");
AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
UsernamePasswordAuthenticationProcessingFilter filter = new UsernamePasswordAuthenticationProcessingFilter();
filter.setAuthenticationManager(createAuthenticationManager());
Authentication result = filter.attemptAuthentication(request, new MockHttpServletResponse());
@@ -113,8 +113,8 @@ public class AuthenticationProcessingFilterTests extends TestCase {
@Test
public void testFailedAuthenticationThrowsException() {
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/");
request.addParameter(AuthenticationProcessingFilter.SPRING_SECURITY_FORM_USERNAME_KEY, "rod");
AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
request.addParameter(UsernamePasswordAuthenticationProcessingFilter.SPRING_SECURITY_FORM_USERNAME_KEY, "rod");
UsernamePasswordAuthenticationProcessingFilter filter = new UsernamePasswordAuthenticationProcessingFilter();
AuthenticationManager am = mock(AuthenticationManager.class);
when(am.authenticate(any(Authentication.class))).thenThrow(new BadCredentialsException(""));
filter.setAuthenticationManager(am);
@@ -127,7 +127,7 @@ public class AuthenticationProcessingFilterTests extends TestCase {
// Check username has still been set
assertEquals("rod", request.getSession().getAttribute(
AuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY));
UsernamePasswordAuthenticationProcessingFilter.SPRING_SECURITY_LAST_USERNAME_KEY));
}
/**
@@ -137,7 +137,7 @@ public class AuthenticationProcessingFilterTests extends TestCase {
public void noSessionIsCreatedIfAllowSessionCreationIsFalse() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest();
AuthenticationProcessingFilter filter = new AuthenticationProcessingFilter();
UsernamePasswordAuthenticationProcessingFilter filter = new UsernamePasswordAuthenticationProcessingFilter();
filter.setAllowSessionCreation(false);
filter.setAuthenticationManager(createAuthenticationManager());
@@ -30,7 +30,7 @@ public class DefaultLoginPageGeneratingFilterTests {
@Test
public void generatingPageWithAuthenticationProcessingFilterOnlyIsSuccessFul() throws Exception {
DefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter(new AuthenticationProcessingFilter());
DefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter(new UsernamePasswordAuthenticationProcessingFilter());
filter.doFilter(new MockHttpServletRequest("GET", "/spring_security_login"), new MockHttpServletResponse(), chain);
filter.doFilter(new MockHttpServletRequest("GET", "/spring_security_login;pathparam=unused"), new MockHttpServletResponse(), chain);
}
@@ -43,7 +43,7 @@ public class DefaultLoginPageGeneratingFilterTests {
}
// Fake OpenID filter (since it's not in this module
private static class MockProcessingFilter extends AbstractProcessingFilter {
private static class MockProcessingFilter extends AbstractAuthenticationProcessingFilter {
protected MockProcessingFilter() {
super("/someurl");
}
@@ -65,14 +65,14 @@ public class DefaultLoginPageGeneratingFilterTests {
/* SEC-1111 */
@Test
public void handlesNonIso8859CharsInErrorMessage() throws Exception {
DefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter(new AuthenticationProcessingFilter());
DefaultLoginPageGeneratingFilter filter = new DefaultLoginPageGeneratingFilter(new UsernamePasswordAuthenticationProcessingFilter());
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/spring_security_login");
request.addParameter("login_error", "true");
MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
String message = messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials", Locale.KOREA);
System.out.println("Message: " + message);
request.getSession().setAttribute(AbstractProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY, new BadCredentialsException(message));
request.getSession().setAttribute(AbstractAuthenticationProcessingFilter.SPRING_SECURITY_LAST_EXCEPTION_KEY, new BadCredentialsException(message));
filter.doFilter(request, new MockHttpServletResponse(), chain);
}
@@ -1,7 +1,7 @@
package org.springframework.security.web.authentication.preauth;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedProcessingFilterEntryPoint;
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
import java.io.IOException;
@@ -21,7 +21,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
public class PreAuthenticatedProcessingFilterEntryPointTests extends TestCase {
public void testGetSetOrder() {
PreAuthenticatedProcessingFilterEntryPoint fep = new PreAuthenticatedProcessingFilterEntryPoint();
Http403ForbiddenEntryPoint fep = new Http403ForbiddenEntryPoint();
fep.setOrder(333);
assertEquals(fep.getOrder(), 333);
}
@@ -29,7 +29,7 @@ public class PreAuthenticatedProcessingFilterEntryPointTests extends TestCase {
public void testCommence() {
MockHttpServletRequest req = new MockHttpServletRequest();
MockHttpServletResponse resp = new MockHttpServletResponse();
PreAuthenticatedProcessingFilterEntryPoint fep = new PreAuthenticatedProcessingFilterEntryPoint();
Http403ForbiddenEntryPoint fep = new Http403ForbiddenEntryPoint();
try {
fep.commence(req,resp,new AuthenticationCredentialsNotFoundException("test"));
assertEquals("Incorrect status",resp.getStatus(),HttpServletResponse.SC_FORBIDDEN);