mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-05-31 01:02:14 +00:00
Add Forward after authentication attempt config support
Fixes gh-3728
This commit is contained in:
parent
dbf73c4692
commit
e33e21fe6b
@ -19,6 +19,8 @@ import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.ForwardAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.ForwardAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.RememberMeServices;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
@ -62,6 +64,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
* </ul>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Shazin Sadakath
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
@ -206,6 +209,28 @@ public final class FormLoginConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward Authentication Failure Handler
|
||||
*
|
||||
* @param forwardUrl the target URL in case of failure
|
||||
* @return he {@link FormLoginConfigurer} for additional customization
|
||||
*/
|
||||
public FormLoginConfigurer<H> failureForwardUrl(String forwardUrl) {
|
||||
failureHandler(new ForwardAuthenticationFailureHandler(forwardUrl));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward Authentication Success Handler
|
||||
*
|
||||
* @param forwardUrl the target URL in case of success
|
||||
* @return he {@link FormLoginConfigurer} for additional customization
|
||||
*/
|
||||
public FormLoginConfigurer<H> successForwardUrl(String forwardUrl) {
|
||||
successHandler(new ForwardAuthenticationSuccessHandler(forwardUrl));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(H http) throws Exception {
|
||||
super.init(http);
|
||||
|
@ -22,9 +22,7 @@ import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.*;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
@ -34,6 +32,7 @@ import org.w3c.dom.Element;
|
||||
* @author Ben Alex
|
||||
* @author Rob Winch
|
||||
* @author Kazuki Shimizu
|
||||
* @author Shazin Sadakath
|
||||
*/
|
||||
public class FormLoginBeanDefinitionParser {
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
@ -55,6 +54,8 @@ public class FormLoginBeanDefinitionParser {
|
||||
|
||||
private static final String ATT_SUCCESS_HANDLER_REF = "authentication-success-handler-ref";
|
||||
private static final String ATT_FAILURE_HANDLER_REF = "authentication-failure-handler-ref";
|
||||
private static final String ATT_FORM_LOGIN_AUTHENTICATION_FAILURE_FORWARD_URL = "authentication-failure-forward-url";
|
||||
private static final String ATT_FORM_LOGIN_AUTHENTICATION_SUCCESS_FORWARD_URL = "authentication-success-forward-url";
|
||||
|
||||
private final String defaultLoginProcessingUrl;
|
||||
private final String filterClassName;
|
||||
@ -95,6 +96,8 @@ public class FormLoginBeanDefinitionParser {
|
||||
String usernameParameter = null;
|
||||
String passwordParameter = null;
|
||||
String authDetailsSourceRef = null;
|
||||
String authenticationFailureForwardUrl = null;
|
||||
String authenticationSuccessForwardUrl = null;
|
||||
|
||||
Object source = null;
|
||||
|
||||
@ -113,6 +116,10 @@ public class FormLoginBeanDefinitionParser {
|
||||
failureHandlerRef = elt.getAttribute(ATT_FAILURE_HANDLER_REF);
|
||||
authDetailsSourceRef = elt
|
||||
.getAttribute(AuthenticationConfigBuilder.ATT_AUTH_DETAILS_SOURCE_REF);
|
||||
authenticationFailureForwardUrl = elt.getAttribute(ATT_FORM_LOGIN_AUTHENTICATION_FAILURE_FORWARD_URL);
|
||||
WebConfigUtils.validateHttpRedirect(authenticationFailureForwardUrl, pc, source);
|
||||
authenticationSuccessForwardUrl = elt.getAttribute(ATT_FORM_LOGIN_AUTHENTICATION_SUCCESS_FORWARD_URL);
|
||||
WebConfigUtils.validateHttpRedirect(authenticationSuccessForwardUrl, pc, source);
|
||||
|
||||
if (!StringUtils.hasText(loginPage)) {
|
||||
loginPage = null;
|
||||
@ -124,7 +131,7 @@ public class FormLoginBeanDefinitionParser {
|
||||
|
||||
filterBean = createFilterBean(loginUrl, defaultTargetUrl, alwaysUseDefault,
|
||||
loginPage, authenticationFailureUrl, successHandlerRef,
|
||||
failureHandlerRef, authDetailsSourceRef);
|
||||
failureHandlerRef, authDetailsSourceRef, authenticationFailureForwardUrl, authenticationSuccessForwardUrl);
|
||||
|
||||
if (StringUtils.hasText(usernameParameter)) {
|
||||
filterBean.getPropertyValues().addPropertyValue("usernameParameter",
|
||||
@ -152,7 +159,7 @@ public class FormLoginBeanDefinitionParser {
|
||||
private RootBeanDefinition createFilterBean(String loginUrl, String defaultTargetUrl,
|
||||
String alwaysUseDefault, String loginPage, String authenticationFailureUrl,
|
||||
String successHandlerRef, String failureHandlerRef,
|
||||
String authDetailsSourceRef) {
|
||||
String authDetailsSourceRef, String authenticationFailureForwardUrl, String authenticationSuccessForwardUrl) {
|
||||
|
||||
BeanDefinitionBuilder filterBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(filterClassName);
|
||||
@ -176,8 +183,12 @@ public class FormLoginBeanDefinitionParser {
|
||||
if (StringUtils.hasText(successHandlerRef)) {
|
||||
filterBuilder.addPropertyReference("authenticationSuccessHandler",
|
||||
successHandlerRef);
|
||||
}
|
||||
else {
|
||||
} else if(StringUtils.hasText(authenticationSuccessForwardUrl)) {
|
||||
BeanDefinitionBuilder forwardSuccessHandler = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(ForwardAuthenticationSuccessHandler.class);
|
||||
forwardSuccessHandler.addConstructorArgValue(authenticationSuccessForwardUrl);
|
||||
filterBuilder.addPropertyValue("authenticationSuccessHandler", forwardSuccessHandler.getBeanDefinition());
|
||||
} else {
|
||||
BeanDefinitionBuilder successHandler = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(SavedRequestAwareAuthenticationSuccessHandler.class);
|
||||
if ("true".equals(alwaysUseDefault)) {
|
||||
@ -205,8 +216,12 @@ public class FormLoginBeanDefinitionParser {
|
||||
if (StringUtils.hasText(failureHandlerRef)) {
|
||||
filterBuilder.addPropertyReference("authenticationFailureHandler",
|
||||
failureHandlerRef);
|
||||
}
|
||||
else {
|
||||
} else if(StringUtils.hasText(authenticationFailureForwardUrl)) {
|
||||
BeanDefinitionBuilder forwardFailureHandler = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(ForwardAuthenticationFailureHandler.class);
|
||||
forwardFailureHandler.addConstructorArgValue(authenticationFailureForwardUrl);
|
||||
filterBuilder.addPropertyValue("authenticationFailureHandler", forwardFailureHandler.getBeanDefinition());
|
||||
} else {
|
||||
BeanDefinitionBuilder failureHandler = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(SimpleUrlAuthenticationFailureHandler.class);
|
||||
if (!StringUtils.hasText(authenticationFailureUrl)) {
|
||||
|
@ -438,6 +438,12 @@ form-login.attlist &=
|
||||
form-login.attlist &=
|
||||
## Reference to an AuthenticationDetailsSource which will be used by the authentication filter
|
||||
attribute authentication-details-source-ref {xsd:token}?
|
||||
form-login.attlist &=
|
||||
## The URL for the ForwardAuthenticationFailureHandler
|
||||
attribute authentication-failure-forward-url {xsd:token}?
|
||||
form-login.attlist &=
|
||||
## The URL for the ForwardAuthenticationSuccessHandler
|
||||
attribute authentication-success-forward-url {xsd:token}?
|
||||
|
||||
|
||||
openid-login =
|
||||
|
@ -1465,6 +1465,18 @@
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="authentication-failure-forward-url" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The URL for the ForwardAuthenticationFailureHandler
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="authentication-success-forward-url" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The URL for the ForwardAuthenticationSuccessHandler
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
<xs:element name="attribute-exchange">
|
||||
|
@ -17,6 +17,7 @@ package org.springframework.security.config.annotation.web.configurers
|
||||
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.http.HttpMethod
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
@ -31,6 +32,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||
import org.springframework.security.web.AuthenticationEntryPoint
|
||||
import org.springframework.security.web.FilterChainProxy
|
||||
import org.springframework.security.web.PortMapper
|
||||
import org.springframework.security.web.WebAttributes
|
||||
import org.springframework.security.web.access.ExceptionTranslationFilter
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter
|
||||
@ -281,6 +283,55 @@ class FormLoginConfigurerTests extends BaseSpringSpec {
|
||||
findFilter(UsernamePasswordAuthenticationFilter).usernameParameter == "custom-username"
|
||||
}
|
||||
|
||||
def "FormLogin permitAll uses Failure Forward Url when ForwardAuthenticationFailureHandler set"() {
|
||||
setup:
|
||||
loadConfig(FormLoginUserForwardAuthenticationSuccessAndFailureConfig)
|
||||
FilterChainProxy springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "access configured explicit ForwardFailureFailureHandler"
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(servletPath:"/login",method:"POST")
|
||||
request.setParameter("username", "user");
|
||||
request.setParameter("password", "invalidpassword");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
springSecurityFilterChain.doFilter(request,response,new MockFilterChain())
|
||||
then: "access is granted to the failure handler"
|
||||
response.status == 200
|
||||
response.forwardedUrl == "/failure_forward_url"
|
||||
request.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) != null
|
||||
}
|
||||
|
||||
def "FormLogin permitAll uses Success Forward Url when ForwardAuthenticationSuccessHandler set"() {
|
||||
setup:
|
||||
loadConfig(FormLoginUserForwardAuthenticationSuccessAndFailureConfig)
|
||||
FilterChainProxy springSecurityFilterChain = context.getBean(FilterChainProxy)
|
||||
when: "access configured explicit ForwardSuccessAuthenticationHandler"
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(servletPath:"/login",method:"POST")
|
||||
request.setParameter("username", "user");
|
||||
request.setParameter("password", "password");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
springSecurityFilterChain.doFilter(request,response,new MockFilterChain())
|
||||
then: "access is granted to the success handler"
|
||||
response.status == 200
|
||||
response.forwardedUrl == "/success_forward_url"
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class FormLoginUserForwardAuthenticationSuccessAndFailureConfig extends BaseWebConfig {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) {
|
||||
http.csrf()
|
||||
.disable()
|
||||
.authorizeRequests()
|
||||
.anyRequest().authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
.failureForwardUrl("/failure_forward_url")
|
||||
.successForwardUrl("/success_forward_url")
|
||||
.permitAll()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class DuplicateInvocationsDoesNotOverrideConfig extends BaseWebConfig {
|
||||
static AuthenticationFailureHandler FAILURE_HANDLER
|
||||
|
@ -3,6 +3,7 @@ package org.springframework.security.config.http
|
||||
import org.springframework.mock.web.MockFilterChain
|
||||
import org.springframework.mock.web.MockHttpServletRequest
|
||||
import org.springframework.mock.web.MockHttpServletResponse
|
||||
import org.springframework.security.web.WebAttributes
|
||||
|
||||
/**
|
||||
*
|
||||
@ -110,4 +111,43 @@ class FormLoginBeanDefinitionParserTests extends AbstractHttpConfigTests {
|
||||
</table>
|
||||
</form></body></html>"""
|
||||
}
|
||||
|
||||
def 'form-login forward authentication failure handler'() {
|
||||
setup:
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(method:'POST',servletPath:'/login')
|
||||
request.setParameter("username", "bob")
|
||||
request.setParameter("password", "invalidpassword")
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
MockFilterChain chain = new MockFilterChain()
|
||||
httpAutoConfig {
|
||||
'form-login'('authentication-failure-forward-url':'/failure_forward_url')
|
||||
csrf(disabled:true)
|
||||
}
|
||||
createAppContext()
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getStatus() == 200
|
||||
response.forwardedUrl == "/failure_forward_url"
|
||||
request.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) != null;
|
||||
}
|
||||
|
||||
def 'form-login forward authentication success handler'() {
|
||||
setup:
|
||||
MockHttpServletRequest request = new MockHttpServletRequest(method:'POST',servletPath:'/login')
|
||||
request.setParameter("username", "bob")
|
||||
request.setParameter("password", "bobspassword")
|
||||
MockHttpServletResponse response = new MockHttpServletResponse()
|
||||
MockFilterChain chain = new MockFilterChain()
|
||||
httpAutoConfig {
|
||||
'form-login'('authentication-success-forward-url':'/success_forward_url')
|
||||
csrf(disabled:true)
|
||||
}
|
||||
createAppContext()
|
||||
when:
|
||||
springSecurityFilterChain.doFilter(request,response,chain)
|
||||
then:
|
||||
response.getStatus() == 200
|
||||
response.forwardedUrl == "/success_forward_url"
|
||||
}
|
||||
}
|
||||
|
@ -7626,6 +7626,14 @@ The name of the request parameter which contains the password. Defaults to "pass
|
||||
* **username-parameter**
|
||||
The name of the request parameter which contains the username. Defaults to "username".
|
||||
|
||||
[[nsa-form-login-authentication-success-forward-url]]
|
||||
* **authentication-success-forward-url**
|
||||
Maps a `ForwardAuthenticationSuccessHandler` to `authenticationSuccessHandler` property of `UsernamePasswordAuthenticationFilter`.
|
||||
|
||||
|
||||
[[nsa-form-login-authentication-failure-forward-url]]
|
||||
* **authentication-failure-forward-url**
|
||||
Maps a `ForwardAuthenticationFailureHandler` to `authenticationFailureHandler` property of `UsernamePasswordAuthenticationFilter`.
|
||||
|
||||
[[nsa-http-basic]]
|
||||
==== <http-basic>
|
||||
@ -7826,6 +7834,16 @@ Reference to an AuthenticationFailureHandler bean which should be used to handle
|
||||
The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?login_error and a corresponding filter to render that login failure URL when requested.
|
||||
|
||||
|
||||
[[nsa-openid-login-authentication-success-forward-url]]
|
||||
* **authentication-success-forward-url**
|
||||
Maps a `ForwardAuthenticationSuccessHandler` to `authenticationSuccessHandler` property of `UsernamePasswordAuthenticationFilter`.
|
||||
|
||||
|
||||
[[nsa-openid-login-authentication-failure-forward-url]]
|
||||
* **authentication-failure-forward-url**
|
||||
Maps a `ForwardAuthenticationFailureHandler` to `authenticationFailureHandler` property of `UsernamePasswordAuthenticationFilter`.
|
||||
|
||||
|
||||
[[nsa-openid-login-authentication-success-handler-ref]]
|
||||
* **authentication-success-handler-ref**
|
||||
Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with <<nsa-openid-login-default-target-url,default-target-url>> (or <<nsa-openid-login-always-use-default-target, always-use-default-target>>) as the implementation should always deal with navigation to the subsequent destination
|
||||
|
Loading…
x
Reference in New Issue
Block a user