SEC-1058: Partial refactoring of AbstractProcessingFilter. It now uses the injected SuccssfulAuthenticationHandler strategy instead of managing everything itself. The default implementation is SavedRequestAwareSuccessfulAuthenticationHandler which encapsulates most of the filter's success logic along with the code which was previously in TargetUrlResolver. Removed TargetUrlResolver.

This commit is contained in:
Luke Taylor 2008-12-12 22:30:57 +00:00
parent 6c7d15ee44
commit 10e4d1fe1a
12 changed files with 361 additions and 386 deletions

View File

@ -7,6 +7,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.security.ui.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.ui.webapp.AuthenticationProcessingFilterEntryPoint;
import org.springframework.security.ui.webapp.DefaultLoginPageGeneratingFilter;
import org.springframework.util.StringUtils;
@ -23,24 +24,26 @@ import org.apache.commons.logging.LogFactory;
public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
protected final Log logger = LogFactory.getLog(getClass());
static final String ATT_LOGIN_URL = "login-processing-url";
private static final String ATT_LOGIN_URL = "login-processing-url";
static final String ATT_LOGIN_PAGE = "login-page";
static final String DEF_LOGIN_PAGE = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL;
private static final String DEF_LOGIN_PAGE = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL;
static final String ATT_FORM_LOGIN_TARGET_URL = "default-target-url";
static final String ATT_ALWAYS_USE_DEFAULT_TARGET_URL = "always-use-default-target";
static final String DEF_FORM_LOGIN_TARGET_URL = "/";
private static final String ATT_FORM_LOGIN_TARGET_URL = "default-target-url";
private static final String ATT_ALWAYS_USE_DEFAULT_TARGET_URL = "always-use-default-target";
private static final String DEF_FORM_LOGIN_TARGET_URL = "/";
static final String ATT_FORM_LOGIN_AUTHENTICATION_FAILURE_URL = "authentication-failure-url";
static final String DEF_FORM_LOGIN_AUTHENTICATION_FAILURE_URL = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL + "?" + DefaultLoginPageGeneratingFilter.ERROR_PARAMETER_NAME;
private static final String ATT_FORM_LOGIN_AUTHENTICATION_FAILURE_URL = "authentication-failure-url";
private static final String DEF_FORM_LOGIN_AUTHENTICATION_FAILURE_URL = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL + "?" + DefaultLoginPageGeneratingFilter.ERROR_PARAMETER_NAME;
String defaultLoginProcessingUrl;
String filterClassName;
private static final String ATT_SUCCESS_HANDLER_REF = "authentication-success-handler-ref";
RootBeanDefinition filterBean;
RootBeanDefinition entryPointBean;
String loginPage;
private String defaultLoginProcessingUrl;
private String filterClassName;
private RootBeanDefinition filterBean;
private RootBeanDefinition entryPointBean;
private String loginPage;
FormLoginBeanDefinitionParser(String defaultLoginProcessingUrl, String filterClassName) {
this.defaultLoginProcessingUrl = defaultLoginProcessingUrl;
@ -52,6 +55,7 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
String defaultTargetUrl = null;
String authenticationFailureUrl = null;
String alwaysUseDefault = null;
String successHandlerRef = null;
Object source = null;
@ -77,6 +81,7 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
ConfigUtils.validateHttpRedirect(authenticationFailureUrl, pc, source);
alwaysUseDefault = elt.getAttribute(ATT_ALWAYS_USE_DEFAULT_TARGET_URL);
loginPage = elt.getAttribute(ATT_LOGIN_PAGE);
successHandlerRef = elt.getAttribute(ATT_SUCCESS_HANDLER_REF);
if (!StringUtils.hasText(loginPage)) {
loginPage = null;
@ -87,7 +92,8 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
ConfigUtils.registerProviderManagerIfNecessary(pc);
filterBean = createFilterBean(loginUrl, defaultTargetUrl, alwaysUseDefault, loginPage, authenticationFailureUrl);
filterBean = createFilterBean(loginUrl, defaultTargetUrl, alwaysUseDefault, loginPage, authenticationFailureUrl,
successHandlerRef);
filterBean.setSource(source);
filterBean.getPropertyValues().addPropertyValue("authenticationManager",
new RuntimeBeanReference(BeanIds.AUTHENTICATION_MANAGER));
@ -117,7 +123,7 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
}
private RootBeanDefinition createFilterBean(String loginUrl, String defaultTargetUrl, String alwaysUseDefault,
String loginPage, String authenticationFailureUrl) {
String loginPage, String authenticationFailureUrl, String successHandlerRef) {
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder.rootBeanDefinition(filterClassName);
@ -125,18 +131,19 @@ public class FormLoginBeanDefinitionParser implements BeanDefinitionParser {
loginUrl = defaultLoginProcessingUrl;
}
if ("true".equals(alwaysUseDefault)) {
filterBuilder.addPropertyValue("alwaysUseDefaultTargetUrl", Boolean.TRUE);
}
filterBuilder.addPropertyValue("filterProcessesUrl", loginUrl);
if (!StringUtils.hasText(defaultTargetUrl)) {
defaultTargetUrl = DEF_FORM_LOGIN_TARGET_URL;
if (StringUtils.hasText(successHandlerRef)) {
filterBuilder.addPropertyReference("successHandler", successHandlerRef);
} else {
BeanDefinitionBuilder successHandler = BeanDefinitionBuilder.rootBeanDefinition(SavedRequestAwareAuthenticationSuccessHandler.class);
if ("true".equals(alwaysUseDefault)) {
successHandler.addPropertyValue("alwaysUseDefaultTargetUrl", Boolean.TRUE);
}
successHandler.addPropertyValue("defaultTargetUrl", StringUtils.hasText(defaultTargetUrl) ? defaultTargetUrl : DEF_FORM_LOGIN_TARGET_URL);
filterBuilder.addPropertyValue("successHandler", successHandler.getBeanDefinition());
}
filterBuilder.addPropertyValue("defaultTargetUrl", defaultTargetUrl);
if (!StringUtils.hasText(authenticationFailureUrl)) {
// Fallback to redisplaying the custom login page, if one was specified
if (StringUtils.hasText(loginPage)) {

View File

@ -66,15 +66,6 @@ import javax.servlet.http.HttpSession;
* <p>
* To use this filter, it is necessary to specify the following properties:
* <ul>
* <li><code>defaultTargetUrl</code> indicates the URL that should be used
* for redirection if the <code>HttpSession</code> attribute named
* {@link SavedRequest#SPRING_SECURITY_SAVED_REQUEST_KEY} does not indicate the target URL once
* authentication is completed successfully. eg: <code>/</code>. The
* <code>defaultTargetUrl</code> will be treated as relative to the web-app's
* context path, and should include the leading <code>/</code>.
* Alternatively, inclusion of a scheme name (eg http:// or https://) as the
* prefix will denote a fully-qualified URL and this is also supported. More
* complex behaviour can be implemented by using a customised {@link TargetUrlResolver}.</li>
* <li><code>authenticationFailureUrl</code> (optional) indicates the URL that should be
* used for redirection if the authentication request fails. eg:
* <code>/login.jsp?login_error=1</code>. If not configured, <tt>sendError</tt> will be
@ -152,31 +143,15 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
*/
private RememberMeServices rememberMeServices = null;
private TargetUrlResolver targetUrlResolver = new TargetUrlResolverImpl();
/** Where to redirect the browser to if authentication fails */
private String authenticationFailureUrl;
/**
* Where to redirect the browser to if authentication is successful and no saved request is stored
* in the session.
*/
private String defaultTargetUrl;
/**
* The URL destination that this filter intercepts and processes (usually
* something like <code>/j_spring_security_check</code>)
*/
private String filterProcessesUrl = getDefaultFilterProcessesUrl();
/**
* If <code>true</code>, will always redirect to the value of
* {@link #getDefaultTargetUrl} upon successful authentication, irrespective
* of the page that caused the authentication request (defaults to
* <code>false</code>).
*/
private boolean alwaysUseDefaultTargetUrl = false;
/**
* Indicates if the filter chain should be continued prior to delegation to
* {@link #successfulAuthentication(HttpServletRequest, HttpServletResponse,
@ -189,7 +164,7 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
* If true, causes any redirection URLs to be calculated minus the protocol
* and context path (defaults to false).
*/
private boolean useRelativeContext = false;
protected boolean useRelativeContext = false;
/**
@ -203,7 +178,7 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
* 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 happpen unless {@link #invalidateSessionOnSuccessfulAuthentication}
* nothing will happen unless {@link #invalidateSessionOnSuccessfulAuthentication}
* is true.
*/
private boolean migrateInvalidatedSessionAttributes = true;
@ -214,16 +189,16 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
private SessionRegistry sessionRegistry;
private AuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
private AuthenticationFailureHandler failureHandler = null;
//~ 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.hasLength(defaultTargetUrl, "defaultTargetUrl must be specified");
Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultTargetUrl), defaultTargetUrl + " isn't a valid redirect URL");
Assert.isTrue(UrlUtils.isValidRedirectUrl(authenticationFailureUrl), authenticationFailureUrl + " isn't a valid redirect URL");
Assert.notNull(authenticationManager, "authenticationManager must be specified");
Assert.notNull(targetUrlResolver, "targetUrlResolver cannot be null");
if (rememberMeServices == null) {
rememberMeServices = new NullRememberMeServices();
@ -245,45 +220,41 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException,
ServletException {
if (requiresAuthentication(request, response)) {
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
try {
onPreAuthentication(request, response);
authResult = attemptAuthentication(request);
}
catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(request, response, failed);
return;
}
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, authResult);
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
chain.doFilter(request, response);
if (logger.isDebugEnabled()) {
logger.debug("Request is to process authentication");
}
Authentication authResult;
try {
onPreAuthentication(request, response);
authResult = attemptAuthentication(request);
}
catch (AuthenticationException failed) {
// Authentication failed
unsuccessfulAuthentication(request, response, failed);
return;
}
// Authentication success
if (continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, authResult);
}
protected void onPreAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException {
}
protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authResult) throws IOException {
}
protected void onUnsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException {
}
@ -326,35 +297,20 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
return uri.endsWith(request.getContextPath() + filterProcessesUrl);
}
protected void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url)
throws IOException {
RedirectUtils.sendRedirect(request, response, url, useRelativeContext);
}
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authResult) throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success: " + authResult.toString());
logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
if (logger.isDebugEnabled()) {
logger.debug("Updated SecurityContextHolder to contain the following Authentication: '" + authResult + "'");
}
if (invalidateSessionOnSuccessfulAuthentication) {
SessionUtils.startNewSessionIfRequired(request, migrateInvalidatedSessionAttributes, sessionRegistry);
}
String targetUrl = determineTargetUrl(request);
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to target URL from HTTP Session (or default): " + targetUrl);
}
onSuccessfulAuthentication(request, response, authResult);
successHandler.onAuthenticationSuccess(request, response, authResult);
rememberMeServices.loginSuccess(request, response, authResult);
@ -362,20 +318,6 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
sendRedirect(request, response, targetUrl);
}
protected String determineTargetUrl(HttpServletRequest request) {
// Don't attempt to obtain the url from the saved request if alwaysUsedefaultTargetUrl is set
String targetUrl = alwaysUseDefaultTargetUrl ? null :
targetUrlResolver.determineTargetUrl(request, SecurityContextHolder.getContext().getAuthentication());
if (targetUrl == null) {
targetUrl = getDefaultTargetUrl();
}
return targetUrl;
}
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
@ -411,7 +353,7 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
} else if (serverSideRedirect){
request.getRequestDispatcher(failureUrl).forward(request, response);
} else {
sendRedirect(request, response, failureUrl);
RedirectUtils.sendRedirect(request, response, failureUrl, useRelativeContext);
}
}
@ -443,25 +385,6 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
*/
public abstract String getDefaultFilterProcessesUrl();
/**
* Supplies the default target Url that will be used if no saved request is
* found or the <tt>alwaysUseDefaultTargetUrl</tt> propert is set to true.
* Override this method of you want to provide a customized default Url (for
* example if you want different Urls depending on the authorities of the
* user who has just logged in).
*
* @return the defaultTargetUrl property
*/
public String getDefaultTargetUrl() {
return defaultTargetUrl;
}
public void setDefaultTargetUrl(String defaultTargetUrl) {
Assert.isTrue(defaultTargetUrl.startsWith("/") | defaultTargetUrl.startsWith("http"),
"defaultTarget must start with '/' or with 'http(s)'");
this.defaultTargetUrl = defaultTargetUrl;
}
protected Properties getExceptionMappings() {
return new Properties(exceptionMappings);
}
@ -486,14 +409,6 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
this.rememberMeServices = rememberMeServices;
}
boolean isAlwaysUseDefaultTargetUrl() {
return alwaysUseDefaultTargetUrl;
}
public void setAlwaysUseDefaultTargetUrl(boolean alwaysUseDefaultTargetUrl) {
this.alwaysUseDefaultTargetUrl = alwaysUseDefaultTargetUrl;
}
public void setContinueChainBeforeSuccessfulAuthentication(boolean continueChainBeforeSuccessfulAuthentication) {
this.continueChainBeforeSuccessfulAuthentication = continueChainBeforeSuccessfulAuthentication;
}
@ -536,20 +451,6 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
this.allowSessionCreation = allowSessionCreation;
}
/**
* @return the targetUrlResolver
*/
protected TargetUrlResolver getTargetUrlResolver() {
return targetUrlResolver;
}
/**
* @param targetUrlResolver the targetUrlResolver to set
*/
public void setTargetUrlResolver(TargetUrlResolver targetUrlResolver) {
this.targetUrlResolver = targetUrlResolver;
}
/**
* Tells if we are to do a server side include of the error URL instead of a 302 redirect.
*
@ -566,4 +467,12 @@ public abstract class AbstractProcessingFilter extends SpringSecurityFilter impl
public void setSessionRegistry(SessionRegistry sessionRegistry) {
this.sessionRegistry = sessionRegistry;
}
public void setSuccessHandler(AuthenticationSuccessHandler successHandler) {
this.successHandler = successHandler;
}
public void setFailureHandler(AuthenticationFailureHandler failureHandler) {
this.failureHandler = failureHandler;
}
}

View File

@ -1,5 +1,8 @@
package org.springframework.security.ui;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -27,6 +30,7 @@ public interface AuthenticationSuccessHandler {
* @param response the response
* @param authentication the <tt>Authentication</tt> object which was created during the authentication process.
*/
void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication);
void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException;
}

View File

@ -0,0 +1,219 @@
package org.springframework.security.ui;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.Authentication;
import org.springframework.security.ui.savedrequest.SavedRequest;
import org.springframework.security.util.RedirectUtils;
import org.springframework.security.wrapper.SavedRequestAwareWrapper;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Decides on the redirect destination following a successful authentication, based on the following
* configuration options:
*
* <ul>
* <li>
* If the <tt>alwaysUseDefaultTargetUrl</tt> property is set to true, the <tt>defaultTargetUrl</tt>
* will be used for the destination. Any <tt>SavedRequest</tt> stored in the session will be
* removed.
* </li>
* <li>
* If the <tt>targetUrlParameter</tt> has been set on the request, the value will be used as the destination.
* Any <tt>SavedRequest</tt> will again be removed.
* </li>
* <li>
* If a {@link SavedRequest} is found in the session (as set by the {@link ExceptionTranslationFilter} to record
* the original destination before the authentication process commenced), a redirect will be performed to the
* Url of that original destination. The <tt>SavedRequest</tt> object will remain in the session and be picked up
* when the redirected request is received (See {@link SavedRequestAwareWrapper}).
* </li>
* <li>
* Fall back to the <tt>defaultTargetUrl</tt>
* </li>
* </ul>
*
*
* @author Luke Taylor
* @version $Id$
* @since 2.5
*/
public class SavedRequestAwareAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
public static String DEFAULT_TARGET_PARAMETER = "spring-security-redirect";
protected final Log logger = LogFactory.getLog(this.getClass());
/* SEC-213 */
private String targetUrlParameter = DEFAULT_TARGET_PARAMETER;
/**
* If <code>true</code>, will only use <code>SavedRequest</code> to determine the target URL on successful
* authentication if the request that caused the authentication request was a GET.
* It will then return null for a POST/PUT request.
* Defaults to false.
*/
private boolean justUseSavedRequestOnGet = false;
private String defaultTargetUrl = "/";
/**
* If <code>true</code>, will always redirect to the value of
* {@link #getDefaultTargetUrl} upon successful authentication, irrespective
* of the page that caused the authentication request (defaults to
* <code>false</code>).
*/
private boolean alwaysUseDefaultTargetUrl = false;
/**
* If true, causes any redirection URLs to be calculated minus the protocol
* and context path (defaults to false).
*/
private boolean useRelativeContext = false;
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
if (alwaysUseDefaultTargetUrl) {
removeSavedRequest(request);
RedirectUtils.sendRedirect(request, response, defaultTargetUrl, useRelativeContext);
return;
}
// Check for the parameter and use that if available
String targetUrl = request.getParameter(targetUrlParameter);
if (StringUtils.hasText(targetUrl)) {
try {
targetUrl = URLDecoder.decode(targetUrl, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 not supported. Shouldn't be possible");
}
logger.debug("Found targetUrlParameter in request. Redirecting to: " + targetUrl);
removeSavedRequest(request);
RedirectUtils.sendRedirect(request, response, targetUrl, useRelativeContext);
return;
}
// Try the SavedRequest URL
SavedRequest savedRequest = getSavedRequest(request);
if (savedRequest != null) {
if (!justUseSavedRequestOnGet || savedRequest.getMethod().equals("GET")) {
targetUrl = savedRequest.getFullRequestUrl();
logger.debug("Redirecting to SavedRequest Url: " + targetUrl);
} else {
removeSavedRequest(request);
}
}
if (targetUrl == null) {
targetUrl = defaultTargetUrl;
logger.debug("Redirecting to default Url: " + targetUrl);
}
RedirectUtils.sendRedirect(request, response, targetUrl, useRelativeContext);
}
private SavedRequest getSavedRequest(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
return (SavedRequest) session.getAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY);
}
return null;
}
private void removeSavedRequest(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
logger.debug("Removing SavedRequest from session if present");
session.removeAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY);
}
}
/**
* Supplies the default target Url that will be used if no saved request is found or the
* <tt>alwaysUseDefaultTargetUrl</tt> property is set to true. If not set, defaults to <tt>/</tt>.
*
* @return the defaultTargetUrl property
*/
protected String getDefaultTargetUrl() {
return defaultTargetUrl;
}
/**
* Supplies the default target Url that will be used if no saved request is found in the session, or the
* <tt>alwaysUseDefaultTargetUrl</tt> property is set to true. If not set, defaults to <tt>/</tt>. It
* will be treated as relative to the web-app's context path, and should include the leading <code>/</code>.
* Alternatively, inclusion of a scheme name (such as "http://" or "https://") as the prefix will denote a
* fully-qualified URL and this is also supported.
*
* @param defaultTargetUrl
*/
public void setDefaultTargetUrl(String defaultTargetUrl) {
Assert.isTrue(defaultTargetUrl.startsWith("/") | defaultTargetUrl.startsWith("http"),
"defaultTarget must start with '/' or with 'http(s)'");
this.defaultTargetUrl = defaultTargetUrl;
}
/**
* If <code>true</code>, will always redirect to the value of <tt>defaultTargetUrl</tt>
* (defaults to <code>false</code>).
*/
public void setAlwaysUseDefaultTargetUrl(boolean alwaysUseDefaultTargetUrl) {
this.alwaysUseDefaultTargetUrl = alwaysUseDefaultTargetUrl;
}
boolean isAlwaysUseDefaultTargetUrl() {
return alwaysUseDefaultTargetUrl;
}
/**
* @return <code>true</code> if just GET request will be used
* to determine target URLs, <code>false</code> otherwise.
*/
protected boolean isJustUseSavedRequestOnGet() {
return justUseSavedRequestOnGet;
}
/**
* @param justUseSavedRequestOnGet set to <code>true</code> if
* just GET request will be used to determine target URLs,
* <code>false</code> otherwise.
*/
public void setJustUseSavedRequestOnGet(boolean justUseSavedRequestOnGet) {
this.justUseSavedRequestOnGet = justUseSavedRequestOnGet;
}
/**
* Before checking the SavedRequest, the current request will be checked for this parameter
* and the value used as the target URL if resent.
*
* @param targetUrlParameter the name of the parameter containing the encoded target URL. Defaults
* to "redirect".
*/
public void setTargetUrlParameter(String targetUrlParameter) {
Assert.hasText("targetUrlParamete canot be null or empty");
this.targetUrlParameter = targetUrlParameter;
}
void setUseRelativeContext(boolean useRelativeContext) {
this.useRelativeContext = useRelativeContext;
}
}

View File

@ -1,40 +0,0 @@
/* 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.ui;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.Authentication;
/**
* Used by {@link AbstractProcessingFilter} to determine target URL in case of
* successful authentication.
*
* @author Martino Piccinato
* @version $Id$
* @since 2.0
*
*/
public interface TargetUrlResolver {
/**
* @param currentRequest the current request
* @param auth The authentication token generated after successful authentication
* @return The URL to be used
*/
public String determineTargetUrl(HttpServletRequest currentRequest, Authentication auth);
}

View File

@ -1,120 +0,0 @@
/* 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.ui;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.security.Authentication;
import org.springframework.security.ui.savedrequest.SavedRequest;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Default implementation for {@link TargetUrlResolver}
* <p>
* Returns a target URL based from the contents of the configured <tt>targetUrlParameter</tt> if present on
* the current request. Failing that, the SavedRequest in the session will be used.
*
* @author Martino Piccinato
* @author Luke Taylor
* @version $Id$
* @since 2.0
*
*/
public class TargetUrlResolverImpl implements TargetUrlResolver {
public static String DEFAULT_TARGET_PARAMETER = "spring-security-redirect";
/* SEC-213 */
private String targetUrlParameter = DEFAULT_TARGET_PARAMETER;
/**
* If <code>true</code>, will only use <code>SavedRequest</code> to determine the target URL on successful
* authentication if the request that caused the authentication request was a GET.
* It will then return null for a POST/PUT request.
* Defaults to false.
*/
private boolean justUseSavedRequestOnGet = false;
public String determineTargetUrl(HttpServletRequest currentRequest, Authentication auth) {
String targetUrl = currentRequest.getParameter(targetUrlParameter);
if (StringUtils.hasText(targetUrl)) {
try {
return URLDecoder.decode(targetUrl, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 not supported. Shouldn't be possible");
}
}
SavedRequest savedRequest = getSavedRequest(currentRequest);
if (savedRequest != null) {
if (!justUseSavedRequestOnGet || savedRequest.getMethod().equals("GET")) {
targetUrl = savedRequest.getFullRequestUrl();
}
}
return targetUrl;
}
private static SavedRequest getSavedRequest(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
return null;
}
SavedRequest savedRequest = (SavedRequest) session.getAttribute(SavedRequest.SPRING_SECURITY_SAVED_REQUEST_KEY);
return savedRequest;
}
/**
* @return <code>true</code> if just GET request will be used
* to determine target URLs, <code>false</code> otherwise.
*/
protected boolean isJustUseSavedRequestOnGet() {
return justUseSavedRequestOnGet;
}
/**
* @param justUseSavedRequestOnGet set to <code>true</code> if
* just GET request will be used to determine target URLs,
* <code>false</code> otherwise.
*/
public void setJustUseSavedRequestOnGet(boolean justUseSavedRequestOnGet) {
this.justUseSavedRequestOnGet = justUseSavedRequestOnGet;
}
/**
* Before checking the SavedRequest, the current request will be checked for this parameter
* and the value used as the target URL if resent.
*
* @param targetUrlParameter the name of the parameter containing the encoded target URL. Defaults
* to "redirect".
*/
public void setTargetUrlParameter(String targetUrlParameter) {
Assert.hasText("targetUrlParamete canot be null or empty");
this.targetUrlParameter = targetUrlParameter;
}
}

View File

@ -175,8 +175,8 @@ public class HttpSecurityBeanDefinitionParserTests {
"</http>" + AUTH_PROVIDER_XML);
// These will be matched by the default pattern "/**"
AuthenticationProcessingFilter filter = (AuthenticationProcessingFilter) getFilters("/anything").get(1);
assertEquals("/default", filter.getDefaultTargetUrl());
assertEquals(Boolean.TRUE, FieldUtils.getFieldValue(filter, "alwaysUseDefaultTargetUrl"));
assertEquals("/default", FieldUtils.getFieldValue(filter, "successHandler.defaultTargetUrl"));
assertEquals(Boolean.TRUE, FieldUtils.getFieldValue(filter, "successHandler.alwaysUseDefaultTargetUrl"));
}
@Test(expected=BeanCreationException.class)

View File

@ -54,15 +54,7 @@ import org.springframework.security.util.PortResolverImpl;
* @version $Id$
*/
public class AbstractProcessingFilterTests extends TestCase {
//~ Constructors ===================================================================================================
public AbstractProcessingFilterTests() {
}
public AbstractProcessingFilterTests(String arg0) {
super(arg0);
}
SavedRequestAwareAuthenticationSuccessHandler successHandler;
//~ Methods ========================================================================================================
private MockHttpServletRequest createMockRequest() {
@ -108,6 +100,8 @@ public class AbstractProcessingFilterTests extends TestCase {
protected void setUp() throws Exception {
super.setUp();
successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setDefaultTargetUrl("/logged_in.jsp");
SecurityContextHolder.clearContext();
}
@ -179,7 +173,7 @@ public class AbstractProcessingFilterTests extends TestCase {
// Setup our test object, to grant access
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true);
filter.setFilterProcessesUrl("/j_OTHER_LOCATION");
filter.setDefaultTargetUrl("/logged_in.jsp");
filter.setSuccessHandler(successHandler);
// Test
executeFilterInContainerSimulator(config, filter, request, response, chain);
@ -191,7 +185,6 @@ public class AbstractProcessingFilterTests extends TestCase {
public void testGettersSetters() throws Exception {
AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
filter.setAuthenticationManager(new MockAuthenticationManager());
filter.setDefaultTargetUrl("/default");
filter.setFilterProcessesUrl("/p");
filter.setAuthenticationFailureUrl("/fail");
filter.afterPropertiesSet();
@ -200,24 +193,10 @@ public class AbstractProcessingFilterTests extends TestCase {
filter.setRememberMeServices(new TokenBasedRememberMeServices());
assertEquals(TokenBasedRememberMeServices.class, filter.getRememberMeServices().getClass());
assertTrue(filter.getAuthenticationManager() != null);
assertEquals("/default", filter.getDefaultTargetUrl());
assertEquals("/p", filter.getFilterProcessesUrl());
assertEquals("/fail", filter.getAuthenticationFailureUrl());
}
public void testDefaultUrlMuststartWithSlashOrHttpScheme() {
AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
filter.setDefaultTargetUrl("/acceptableRelativeUrl");
filter.setDefaultTargetUrl("http://some.site.org/index.html");
filter.setDefaultTargetUrl("https://some.site.org/index.html");
try {
filter.setDefaultTargetUrl("missingSlash");
fail("Shouldn't accept default target without leading slash");
} catch (IllegalArgumentException expected) {}
}
public void testIgnoresAnyServletPathOtherThanFilterProcessesUrl() throws Exception {
// Setup our HTTP request
MockHttpServletRequest request = createMockRequest();
@ -252,8 +231,9 @@ public class AbstractProcessingFilterTests extends TestCase {
// Setup our test object, to grant access
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true);
filter.setFilterProcessesUrl("/j_mock_post");
filter.setDefaultTargetUrl("/logged_in.jsp");
filter.setSuccessHandler(successHandler);
filter.setAuthenticationFailureUrl("/failure.jsp");
filter.setAuthenticationManager(new MockAuthenticationManager(true));
filter.afterPropertiesSet();
@ -270,7 +250,8 @@ public class AbstractProcessingFilterTests extends TestCase {
public void testStartupDetectsInvalidAuthenticationManager() throws Exception {
AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
filter.setAuthenticationFailureUrl("/failed.jsp");
filter.setDefaultTargetUrl("/");
successHandler.setDefaultTargetUrl("/");
filter.setSuccessHandler(successHandler);
filter.setFilterProcessesUrl("/j_spring_security_check");
try {
@ -281,25 +262,11 @@ public class AbstractProcessingFilterTests extends TestCase {
}
}
public void testStartupDetectsInvalidDefaultTargetUrl() throws Exception {
AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
filter.setAuthenticationFailureUrl("/failed.jsp");
filter.setAuthenticationManager(new MockAuthenticationManager());
filter.setFilterProcessesUrl("/j_spring_security_check");
try {
filter.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertEquals("defaultTargetUrl must be specified", expected.getMessage());
}
}
public void testStartupDetectsInvalidFilterProcessesUrl() throws Exception {
AbstractProcessingFilter filter = new MockAbstractProcessingFilter();
filter.setAuthenticationFailureUrl("/failed.jsp");
filter.setAuthenticationManager(new MockAuthenticationManager());
filter.setDefaultTargetUrl("/");
filter.setSuccessHandler(successHandler);
filter.setFilterProcessesUrl(null);
try {
@ -324,7 +291,7 @@ public class AbstractProcessingFilterTests extends TestCase {
// Setup our test object, to grant access
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true);
filter.setFilterProcessesUrl("/j_mock_post");
filter.setDefaultTargetUrl("/logged_in.jsp");
filter.setSuccessHandler(successHandler);
// Test
executeFilterInContainerSimulator(config, filter, request, response, chain);
@ -364,10 +331,11 @@ public class AbstractProcessingFilterTests extends TestCase {
// Setup our test object, to grant access
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true);
filter.setFilterProcessesUrl("/j_mock_post");
filter.setDefaultTargetUrl("/foobar");
assertFalse(filter.isAlwaysUseDefaultTargetUrl()); // check default
filter.setAlwaysUseDefaultTargetUrl(true);
assertTrue(filter.isAlwaysUseDefaultTargetUrl()); // check changed
successHandler.setDefaultTargetUrl("/foobar");
assertFalse(successHandler.isAlwaysUseDefaultTargetUrl()); // check default
successHandler.setAlwaysUseDefaultTargetUrl(true);
assertTrue(successHandler.isAlwaysUseDefaultTargetUrl()); // check changed
filter.setSuccessHandler(successHandler);
// Test
executeFilterInContainerSimulator(config, filter, request, response, chain);
@ -413,12 +381,10 @@ public class AbstractProcessingFilterTests extends TestCase {
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true);
filter.setFilterProcessesUrl("/j_mock_post");
filter.setDefaultTargetUrl("/foobar");
// Configure target resolver default implementation not to use POST SavedRequest
TargetUrlResolverImpl targetUrlResolver = new TargetUrlResolverImpl();
targetUrlResolver.setJustUseSavedRequestOnGet(true);
filter.setTargetUrlResolver(targetUrlResolver);
filter.setSuccessHandler(successHandler);
successHandler.setDefaultTargetUrl("/foobar");
// Configure not to use POST SavedRequest
successHandler.setJustUseSavedRequestOnGet(true);
// Test
executeFilterInContainerSimulator(config, filter, request, response, chain);
@ -438,8 +404,9 @@ public class AbstractProcessingFilterTests extends TestCase {
// Setup our test object, to grant access
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true);
filter.setDefaultTargetUrl("https://monkeymachine.co.uk/");
filter.setAlwaysUseDefaultTargetUrl(true);
successHandler.setDefaultTargetUrl("https://monkeymachine.co.uk/");
successHandler.setAlwaysUseDefaultTargetUrl(true);
filter.setSuccessHandler(successHandler);
executeFilterInContainerSimulator(config, filter, request, response, chain);
assertEquals("https://monkeymachine.co.uk/", response.getRedirectedUrl());
@ -458,7 +425,8 @@ public class AbstractProcessingFilterTests extends TestCase {
// Setup our test object, to grant access
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true);
filter.setInvalidateSessionOnSuccessfulAuthentication(true);
filter.setDefaultTargetUrl("http://monkeymachine.co.uk/");
successHandler.setDefaultTargetUrl("http://monkeymachine.co.uk/");
filter.setSuccessHandler(successHandler);
executeFilterInContainerSimulator(config, filter, request, response, chain);
@ -477,7 +445,8 @@ public class AbstractProcessingFilterTests extends TestCase {
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true);
filter.setInvalidateSessionOnSuccessfulAuthentication(true);
filter.setMigrateInvalidatedSessionAttributes(false);
filter.setDefaultTargetUrl("http://monkeymachine.co.uk/");
successHandler.setDefaultTargetUrl("http://monkeymachine.co.uk/");
filter.setSuccessHandler(successHandler);
executeFilterInContainerSimulator(config, filter, request, response, chain);
@ -500,7 +469,8 @@ public class AbstractProcessingFilterTests extends TestCase {
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(false);
filter.setAllowSessionCreation(false);
filter.setAuthenticationFailureUrl("/");
filter.setDefaultTargetUrl("http://monkeymachine.co.uk/");
successHandler.setDefaultTargetUrl("http://monkeymachine.co.uk/");
filter.setSuccessHandler(successHandler);
executeFilterInContainerSimulator(config, filter, request, response, chain);
@ -518,7 +488,8 @@ public class AbstractProcessingFilterTests extends TestCase {
MockHttpServletResponse response = new MockHttpServletResponse();
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(false);
filter.setDefaultTargetUrl("http://monkeymachine.co.uk/");
successHandler.setDefaultTargetUrl("http://monkeymachine.co.uk/");
filter.setSuccessHandler(successHandler);
executeFilterInContainerSimulator(config, filter, request, response, chain);
@ -536,7 +507,8 @@ public class AbstractProcessingFilterTests extends TestCase {
MockHttpServletResponse response = new MockHttpServletResponse();
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(false);
filter.setDefaultTargetUrl("http://monkeymachine.co.uk/");
successHandler.setDefaultTargetUrl("http://monkeymachine.co.uk/");
filter.setSuccessHandler(successHandler);
filter.setAuthenticationFailureUrl("/error");
filter.setServerSideRedirect(true);
@ -557,10 +529,9 @@ public class AbstractProcessingFilterTests extends TestCase {
MockHttpServletResponse response = new MockHttpServletResponse();
MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true);
TargetUrlResolverImpl targetUrlResolver = new TargetUrlResolverImpl();
targetUrlResolver.setTargetUrlParameter("targetUrl");
filter.setTargetUrlResolver(targetUrlResolver);
filter.setDefaultTargetUrl("http://monkeymachine.co.uk/");
filter.setSuccessHandler(successHandler);
successHandler.setDefaultTargetUrl("http://monkeymachine.co.uk/");
successHandler.setTargetUrlParameter("targetUrl");
filter.setAuthenticationFailureUrl("/error");
executeFilterInContainerSimulator(config, filter, request, response, chain);

View File

@ -0,0 +1,22 @@
package org.springframework.security.ui;
import static org.junit.Assert.*;
import org.junit.Test;
public class SavedRequestAwareAuthenticationSuccessHandlerTests {
@Test
public void defaultUrlMuststartWithSlashOrHttpScheme() {
SavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();
handler.setDefaultTargetUrl("/acceptableRelativeUrl");
handler.setDefaultTargetUrl("http://some.site.org/index.html");
handler.setDefaultTargetUrl("https://some.site.org/index.html");
try {
handler.setDefaultTargetUrl("missingSlash");
fail("Shouldn't accept default target without leading slash");
} catch (IllegalArgumentException expected) {}
}
}

View File

@ -28,11 +28,10 @@ http://www.springframework.org/schema/security http://www.springframework.org/sc
<bean id="mockFilter2" class="org.springframework.security.util.MockFilter"/>
<!-- These are just here so we have filters of a specfic type to check the ordering is as expected -->
<!-- These are just here so we have filters of a specific type to check the ordering is as expected -->
<bean id="sif" class="org.springframework.security.context.HttpSessionContextIntegrationFilter"/>
<bean id="apf" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilter">
<property name="defaultTargetUrl" value="/whocares"/>
<property name="authenticationFailureUrl" value="/whocares2"/>
<property name="authenticationManager">
<bean class="org.springframework.security.MockAuthenticationManager"/>

View File

@ -26,6 +26,7 @@ import org.springframework.security.ui.AbstractProcessingFilter;
import org.springframework.security.ui.FilterChainOrder;
import org.springframework.security.ui.openid.consumers.OpenID4JavaConsumer;
import org.springframework.security.ui.webapp.AuthenticationProcessingFilter;
import org.springframework.security.util.RedirectUtils;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
@ -225,7 +226,7 @@ public class OpenIDAuthenticationProcessingFilter extends AbstractProcessingFilt
super.getRememberMeServices().loginFail(request, response);
sendRedirect(request, response, failureUrl);
RedirectUtils.sendRedirect(request, response, failureUrl, useRelativeContext);
}
public int getOrder() {

View File

@ -4,6 +4,7 @@ import junit.framework.TestCase;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.MockAuthenticationManager;
import org.springframework.security.ui.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.ui.openid.consumers.MockOpenIDConsumer;
import org.springframework.security.util.MockFilterChain;
@ -21,7 +22,9 @@ public class OpenIDAuthenticationProcessingFilterTests extends TestCase {
protected void setUp() throws Exception {
filter = new OpenIDAuthenticationProcessingFilter();
filter.setConsumer(new MockOpenIDConsumer(REDIRECT_URL));
filter.setDefaultTargetUrl(DEFAULT_TARGET_URL);
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
filter.setSuccessHandler(new SavedRequestAwareAuthenticationSuccessHandler());
successHandler.setDefaultTargetUrl(DEFAULT_TARGET_URL);
filter.setAuthenticationManager(new MockAuthenticationManager());
filter.afterPropertiesSet();
}