diff --git a/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationFilterTests.java b/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationFilterTests.java index e6ddf8fde2..f5967b33c9 100644 --- a/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationFilterTests.java +++ b/cas/src/test/java/org/springframework/security/cas/web/CasAuthenticationFilterTests.java @@ -172,6 +172,7 @@ public class CasAuthenticationFilterTests { serviceProperties.setAuthenticateAllArtifacts(true); MockHttpServletRequest request = new MockHttpServletRequest(); request.setParameter("ticket", "ST-1-123"); + request.setRequestURI("/authenticate"); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain chain = mock(FilterChain.class); diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java index ade9c4e3b3..3c0a736c0c 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractAuthenticationFilterConfigurer.java @@ -33,6 +33,7 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; +import org.springframework.security.web.util.AntPathRequestMatcher; import org.springframework.security.web.util.MediaTypeRequestMatcher; import org.springframework.security.web.util.RequestMatcher; import org.springframework.web.accept.ContentNegotiationStrategy; @@ -139,10 +140,17 @@ public abstract class AbstractAuthenticationFilterConfigurer> extends * @see HttpSecurity#formLogin() */ public FormLoginConfigurer() { - super(createUsernamePasswordAuthenticationFilter(),"/login"); + super(new UsernamePasswordAuthenticationFilter(),"/login"); usernameParameter("username"); passwordParameter("password"); } @@ -193,6 +192,15 @@ public final class FormLoginConfigurer> extends initDefaultLoginFilter(http); } + /* (non-Javadoc) + * @see org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer#createLoginProcessingUrlMatcher(java.lang.String) + */ + @Override + protected RequestMatcher createLoginProcessingUrlMatcher( + String loginProcessingUrl) { + return new AntPathRequestMatcher(loginProcessingUrl, "POST"); + } + /** * Gets the HTTP parameter that is used to submit the username. * @@ -227,13 +235,4 @@ public final class FormLoginConfigurer> extends loginPageGeneratingFilter.setAuthenticationUrl(getLoginProcessingUrl()); } } - - private static UsernamePasswordAuthenticationFilter createUsernamePasswordAuthenticationFilter() { - return new UsernamePasswordAuthenticationFilter() { - @Override - protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) { - return "POST".equals(request.getMethod()) && super.requiresAuthentication(request, response); - } - }; - } } diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/openid/OpenIDLoginConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/openid/OpenIDLoginConfigurer.java index e5a7c41c2f..7c045a4c1d 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/openid/OpenIDLoginConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/openid/OpenIDLoginConfigurer.java @@ -49,6 +49,8 @@ import org.springframework.security.web.authentication.LoginUrlAuthenticationEnt import org.springframework.security.web.authentication.RememberMeServices; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; import org.springframework.security.web.authentication.ui.DefaultLoginPageViewFilter; +import org.springframework.security.web.util.AntPathRequestMatcher; +import org.springframework.security.web.util.RequestMatcher; /** * Adds support for OpenID based authentication. @@ -248,6 +250,15 @@ public final class OpenIDLoginConfigurer> exten super.configure(http); } + /* (non-Javadoc) + * @see org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer#createLoginProcessingUrlMatcher(java.lang.String) + */ + @Override + protected RequestMatcher createLoginProcessingUrlMatcher( + String loginProcessingUrl) { + return new AntPathRequestMatcher(loginProcessingUrl); + } + /** * Gets the {@link OpenIDConsumer} that was configured or defaults to an {@link OpenID4JavaConsumer}. * @return the {@link OpenIDConsumer} to use diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/BaseWebSpecuritySpec.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/BaseWebSpecuritySpec.groovy index 4fd702636d..29f1c9d6ab 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/BaseWebSpecuritySpec.groovy +++ b/config/src/test/groovy/org/springframework/security/config/annotation/BaseWebSpecuritySpec.groovy @@ -37,7 +37,7 @@ abstract class BaseWebSpecuritySpec extends BaseSpringSpec { MockFilterChain chain def setup() { - request = new MockHttpServletRequest() + request = new MockHttpServletRequest(method:"GET") response = new MockHttpServletResponse() chain = new MockFilterChain() } diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/SampleWebSecurityConfigurerAdapterTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/SampleWebSecurityConfigurerAdapterTests.groovy index b4ce9a23a2..5ef38b733d 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/SampleWebSecurityConfigurerAdapterTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/SampleWebSecurityConfigurerAdapterTests.groovy @@ -46,14 +46,14 @@ public class SampleWebSecurityConfigurerAdapterTests extends BaseWebSpecuritySpe when: "fail to log in" super.setup() request.addHeader("Accept", "text/html") - request.requestURI = "/login" + request.servletPath = "/login" request.method = "POST" springSecurityFilterChain.doFilter(request,response,chain) then: "sent to login error page" response.getRedirectedUrl() == "/login?error" when: "login success" super.setup() - request.requestURI = "/login" + request.servletPath = "/login" request.method = "POST" request.parameters.username = ["user"] as String[] request.parameters.password = ["password"] as String[] @@ -108,14 +108,14 @@ public class SampleWebSecurityConfigurerAdapterTests extends BaseWebSpecuritySpe response.getRedirectedUrl() == "http://localhost/login" when: "fail to log in" super.setup() - request.requestURI = "/login" + request.servletPath = "/login" request.method = "POST" springSecurityFilterChain.doFilter(request,response,chain) then: "sent to login error page" response.getRedirectedUrl() == "/login?error" when: "login success" super.setup() - request.requestURI = "/login" + request.servletPath = "/login" request.method = "POST" request.parameters.username = ["user"] as String[] request.parameters.password = ["password"] as String[] @@ -197,14 +197,14 @@ public class SampleWebSecurityConfigurerAdapterTests extends BaseWebSpecuritySpe response.getRedirectedUrl() == "http://localhost/login" when: "fail to log in" super.setup() - request.requestURI = "/login" + request.servletPath = "/login" request.method = "POST" springSecurityFilterChain.doFilter(request,response,chain) then: "sent to login error page" response.getRedirectedUrl() == "/login?error" when: "login success" super.setup() - request.requestURI = "/login" + request.servletPath = "/login" request.method = "POST" request.parameters.username = ["user"] as String[] request.parameters.password = ["password"] as String[] diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.groovy index f0cb3156da..44f1889354 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.groovy @@ -77,7 +77,7 @@ public class DefaultLoginPageConfigurerTests extends BaseSpringSpec { """ when: "fail to log in" setup() - request.requestURI = "/login" + request.servletPath = "/login" request.method = "POST" springSecurityFilterChain.doFilter(request,response,chain) then: "sent to login error page" @@ -100,7 +100,7 @@ public class DefaultLoginPageConfigurerTests extends BaseSpringSpec { """ when: "login success" setup() - request.requestURI = "/login" + request.servletPath = "/login" request.method = "POST" request.parameters.username = ["user"] as String[] request.parameters.password = ["password"] as String[] diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.groovy index 9393db2c41..51eec00507 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/FormLoginConfigurerTests.groovy @@ -72,8 +72,8 @@ class FormLoginConfigurerTests extends BaseSpringSpec { authFilter.passwordParameter == "password" authFilter.failureHandler.defaultFailureUrl == "/login?error" authFilter.successHandler.defaultTargetUrl == "/" - authFilter.requiresAuthentication(new MockHttpServletRequest(requestURI : "/login", method: "POST"), new MockHttpServletResponse()) - !authFilter.requiresAuthentication(new MockHttpServletRequest(requestURI : "/login", method: "GET"), new MockHttpServletResponse()) + authFilter.requiresAuthentication(new MockHttpServletRequest(servletPath : "/login", method: "POST"), new MockHttpServletResponse()) + !authFilter.requiresAuthentication(new MockHttpServletRequest(servletPath : "/login", method: "GET"), new MockHttpServletResponse()) and: "SessionFixationProtectionStrategy is configured correctly" SessionFixationProtectionStrategy sessionStrategy = ReflectionTestUtils.getField(authFilter,"sessionStrategy") @@ -82,7 +82,7 @@ class FormLoginConfigurerTests extends BaseSpringSpec { and: "Exception handling is configured correctly" AuthenticationEntryPoint authEntryPoint = filterChains[1].filters.find { it instanceof ExceptionTranslationFilter}.authenticationEntryPoint MockHttpServletResponse response = new MockHttpServletResponse() - authEntryPoint.commence(new MockHttpServletRequest(requestURI: "/private/"), response, new BadCredentialsException("")) + authEntryPoint.commence(new MockHttpServletRequest(servletPath: "/private/"), response, new BadCredentialsException("")) response.redirectedUrl == "http://localhost/login" } @@ -172,7 +172,7 @@ class FormLoginConfigurerTests extends BaseSpringSpec { loadConfig(PermitAllIgnoresFailureHandlerConfig) FilterChainProxy springSecurityFilterChain = context.getBean(FilterChainProxy) when: "access default failureUrl and configured explicit FailureHandler" - MockHttpServletRequest request = new MockHttpServletRequest(requestURI:"/login",queryString:"error") + MockHttpServletRequest request = new MockHttpServletRequest(servletPath:"/login",requestURI:"/login",queryString:"error",method:"GET") MockHttpServletResponse response = new MockHttpServletResponse() springSecurityFilterChain.doFilter(request,response,new MockFilterChain()) then: "access is not granted to the failure handler (sent to login page)" diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.groovy index 4ffb9a8a8c..7667f98192 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpFormLoginTests.groovy @@ -50,14 +50,14 @@ public class NamespaceHttpFormLoginTests extends BaseSpringSpec { response.getRedirectedUrl() == "http://localhost/login" when: "fail to log in" super.setup() - request.requestURI = "/login" + request.servletPath = "/login" request.method = "POST" springSecurityFilterChain.doFilter(request,response,chain) then: "sent to login error page" response.getRedirectedUrl() == "/login?error" when: "login success" super.setup() - request.requestURI = "/login" + request.servletPath = "/login" request.method = "POST" request.parameters.username = ["user"] as String[] request.parameters.password = ["password"] as String[] @@ -96,14 +96,14 @@ public class NamespaceHttpFormLoginTests extends BaseSpringSpec { response.getRedirectedUrl() == "http://localhost/authentication/login" when: "fail to log in" super.setup() - request.requestURI = "/authentication/login/process" + request.servletPath = "/authentication/login/process" request.method = "POST" springSecurityFilterChain.doFilter(request,response,chain) then: "sent to login error page" response.getRedirectedUrl() == "/authentication/login?failed" when: "login success" super.setup() - request.requestURI = "/authentication/login/process" + request.servletPath = "/authentication/login/process" request.method = "POST" request.parameters.j_username = ["user"] as String[] request.parameters.j_password = ["password"] as String[] @@ -137,14 +137,14 @@ public class NamespaceHttpFormLoginTests extends BaseSpringSpec { then: "CustomWebAuthenticationDetailsSource is used" findFilter(UsernamePasswordAuthenticationFilter).authenticationDetailsSource.class == CustomWebAuthenticationDetailsSource when: "fail to log in" - request.requestURI = "/login" + request.servletPath = "/login" request.method = "POST" springSecurityFilterChain.doFilter(request,response,chain) then: "sent to login error page" response.getRedirectedUrl() == "/custom/failure" when: "login success" super.setup() - request.requestURI = "/login" + request.servletPath = "/login" request.method = "POST" request.parameters.username = ["user"] as String[] request.parameters.password = ["password"] as String[] diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpOpenIDLoginTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpOpenIDLoginTests.groovy index a72418532c..44c745f7b7 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpOpenIDLoginTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceHttpOpenIDLoginTests.groovy @@ -67,7 +67,7 @@ public class NamespaceHttpOpenIDLoginTests extends BaseSpringSpec { response.getRedirectedUrl() == "http://localhost/login" when: "fail to log in" setup() - request.requestURI = "/login/openid" + request.servletPath = "/login/openid" request.method = "POST" springSecurityFilterChain.doFilter(request,response,chain) then: "sent to login error page" @@ -118,7 +118,7 @@ public class NamespaceHttpOpenIDLoginTests extends BaseSpringSpec { response.getRedirectedUrl() == "http://localhost/login" when: "fail to log in" setup() - request.requestURI = "/login/openid" + request.servletPath = "/login/openid" request.method = "POST" springSecurityFilterChain.doFilter(request,response,chain) then: "sent to login error page" @@ -172,7 +172,7 @@ public class NamespaceHttpOpenIDLoginTests extends BaseSpringSpec { response.getRedirectedUrl() == "http://localhost/authentication/login" when: "fail to log in" setup() - request.requestURI = "/authentication/login/process" + request.servletPath = "/authentication/login/process" request.method = "POST" springSecurityFilterChain.doFilter(request,response,chain) then: "sent to login error page" @@ -205,7 +205,7 @@ public class NamespaceHttpOpenIDLoginTests extends BaseSpringSpec { findFilter(OpenIDAuthenticationFilter).authenticationDetailsSource.class == CustomWebAuthenticationDetailsSource findAuthenticationProvider(OpenIDAuthenticationProvider).userDetailsService == OpenIDLoginCustomRefsConfig.AUDS when: "fail to log in" - request.requestURI = "/login/openid" + request.servletPath = "/login/openid" request.method = "POST" springSecurityFilterChain.doFilter(request,response,chain) then: "sent to login error page" diff --git a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.groovy b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.groovy index 26a4524bf0..9a4f116b94 100644 --- a/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.groovy +++ b/config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/NamespaceRememberMeTests.groovy @@ -54,24 +54,13 @@ import org.springframework.test.util.ReflectionTestUtils; * */ public class NamespaceRememberMeTests extends BaseSpringSpec { - FilterChainProxy springSecurityFilterChain - MockHttpServletRequest request - MockHttpServletResponse response - MockFilterChain chain - - def setup() { - request = new MockHttpServletRequest() - response = new MockHttpServletResponse() - chain = new MockFilterChain() - } def "http/remember-me"() { setup: loadConfig(RememberMeConfig) - springSecurityFilterChain = context.getBean(FilterChainProxy) when: "login with remember me" - setup() - request.requestURI = "/login" + super.setup() + request.servletPath = "/login" request.method = "POST" request.parameters.username = ["user"] as String[] request.parameters.password = ["password"] as String[] @@ -81,7 +70,7 @@ public class NamespaceRememberMeTests extends BaseSpringSpec { then: "response contains remember me cookie" rememberMeCookie != null when: "session expires" - setup() + super.setup() request.setCookies(rememberMeCookie) request.requestURI = "/abc" springSecurityFilterChain.doFilter(request,response,chain) @@ -90,7 +79,7 @@ public class NamespaceRememberMeTests extends BaseSpringSpec { SecurityContext context = new HttpSessionSecurityContextRepository().loadContext(new HttpRequestResponseHolder(request, response)) context.getAuthentication() instanceof RememberMeAuthenticationToken when: "logout" - setup() + super.setup() request.setSession(session) request.setCookies(rememberMeCookie) request.requestURI = "/logout" @@ -100,7 +89,7 @@ public class NamespaceRememberMeTests extends BaseSpringSpec { response.getRedirectedUrl() == "/login?logout" rememberMeCookie.maxAge == 0 when: "use remember me after logout" - setup() + super.setup() request.setCookies(rememberMeCookie) request.requestURI = "/abc" springSecurityFilterChain.doFilter(request,response,chain) diff --git a/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java b/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java index 9d71972066..0eefd62225 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java @@ -40,6 +40,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.WebAttributes; import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; +import org.springframework.security.web.util.RequestMatcher; import org.springframework.security.web.util.UrlUtils; import org.springframework.util.Assert; import org.springframework.web.filter.GenericFilterBean; @@ -53,8 +54,7 @@ import org.springframework.web.filter.GenericFilterBean; * required to process the authentication request tokens created by implementing classes. *

* This filter will intercept a request and attempt to perform authentication from that request if - * the request URL matches the value of the filterProcessesUrl property. This behaviour can modified by - * overriding the method {@link #requiresAuthentication(HttpServletRequest, HttpServletResponse) requiresAuthentication}. + * the request matches the {@link #setRequiresAuthenticationRequestMatcher(RequestMatcher)}. *

* Authentication is performed by the {@link #attemptAuthentication(HttpServletRequest, HttpServletResponse) * attemptAuthentication} method, which must be implemented by subclasses. @@ -116,10 +116,14 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); private RememberMeServices rememberMeServices = new NullRememberMeServices(); + private RequestMatcher requiresAuthenticationRequestMatcher; + /** * The URL destination that this filter intercepts and processes (usually * something like /j_spring_security_check) + * @deprecated use {@link #requiresAuthenticationRequestMatcher} instead */ + @Deprecated private String filterProcessesUrl; private boolean continueChainBeforeSuccessfulAuthentication = false; @@ -137,15 +141,26 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt * @param defaultFilterProcessesUrl the default value for filterProcessesUrl. */ protected AbstractAuthenticationProcessingFilter(String defaultFilterProcessesUrl) { + this.requiresAuthenticationRequestMatcher = new FilterProcessUrlRequestMatcher(defaultFilterProcessesUrl); this.filterProcessesUrl = defaultFilterProcessesUrl; } + /** + * Creates a new instance + * + * @param requiresAuthenticationRequestMatcher + * the {@link RequestMatcher} used to determine if authentication + * is required. Cannot be null. + */ + protected AbstractAuthenticationProcessingFilter(RequestMatcher requiresAuthenticationRequestMatcher) { + Assert.notNull(requiresAuthenticationRequestMatcher, "requiresAuthenticationRequestMatcher cannot be null"); + this.requiresAuthenticationRequestMatcher = requiresAuthenticationRequestMatcher; + } + //~ Methods ======================================================================================================== @Override public void afterPropertiesSet() { - 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) { @@ -231,21 +246,11 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt * Subclasses may override for special requirements, such as Tapestry integration. * * @return true if the filter should attempt authentication, false otherwise. + * @deprecated use {@link #setRequiresAuthenticationRequestMatcher(RequestMatcher)} instead */ + @Deprecated 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); + return requiresAuthenticationRequestMatcher.matches(request); } /** @@ -358,14 +363,29 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt this.authenticationManager = authenticationManager; } + @Deprecated public String getFilterProcessesUrl() { return filterProcessesUrl; } + /** + * Sets the URL that determines if authentication is required + * + * @param filterProcessesUrl + * @deprecated use {@link #setRequiresAuthenticationRequestMatcher(RequestMatcher)} instead + */ + @Deprecated public void setFilterProcessesUrl(String filterProcessesUrl) { + this.requiresAuthenticationRequestMatcher = new FilterProcessUrlRequestMatcher(filterProcessesUrl); this.filterProcessesUrl = filterProcessesUrl; } + public final void setRequiresAuthenticationRequestMatcher(RequestMatcher requestMatcher) { + Assert.notNull(requestMatcher, "requestMatcher cannot be null"); + this.filterProcessesUrl = null; + this.requiresAuthenticationRequestMatcher = requestMatcher; + } + public RememberMeServices getRememberMeServices() { return rememberMeServices; } @@ -439,4 +459,31 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt protected AuthenticationFailureHandler getFailureHandler() { return failureHandler; } + + private static final class FilterProcessUrlRequestMatcher implements RequestMatcher { + private final String filterProcessesUrl; + + private FilterProcessUrlRequestMatcher(String filterProcessesUrl) { + Assert.hasLength(filterProcessesUrl, "filterProcessesUrl must be specified"); + Assert.isTrue(UrlUtils.isValidRedirectUrl(filterProcessesUrl), filterProcessesUrl + " isn't a valid redirect URL"); + this.filterProcessesUrl = filterProcessesUrl; + } + + @Override + public boolean matches(HttpServletRequest request) { + 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); + } + } } diff --git a/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLoginPageViewFilter.java b/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLoginPageViewFilter.java index 443d560fe6..bb945f6ad5 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLoginPageViewFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLoginPageViewFilter.java @@ -30,7 +30,7 @@ import org.springframework.security.web.WebAttributes; import org.springframework.web.filter.GenericFilterBean; /** - * This class generates a default login page if one was not specified. The class is quite similar + * This class generates a default login page if one was not specified. * * @author Rob Winch * @since 3.2 @@ -202,7 +202,7 @@ public class DefaultLoginPageViewFilter extends GenericFilterBean { } private boolean matches(HttpServletRequest request, String url) { - if(!"GET".equals(request.getMethod())) { + if(!"GET".equals(request.getMethod()) || url == null) { return false; } String uri = request.getRequestURI(); diff --git a/web/src/test/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilterTests.java index 21be453b4e..43618a2c94 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilterTests.java @@ -213,10 +213,9 @@ public class AbstractAuthenticationProcessingFilterTests { filter.setAuthenticationFailureHandler(failureHandler); filter.setAuthenticationManager(mock(AuthenticationManager.class)); filter.setAuthenticationSuccessHandler(successHandler); - filter.setFilterProcessesUrl(null); try { - filter.afterPropertiesSet(); + filter.setFilterProcessesUrl(null); fail("Should have thrown IllegalArgumentException"); } catch (IllegalArgumentException expected) { assertEquals("filterProcessesUrl must be specified", expected.getMessage());