diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java
index 565acac30b..341515f5c3 100644
--- a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java
+++ b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java
@@ -681,6 +681,45 @@ public final class HttpSecurity extends
return getOrApply(new ExceptionHandlingConfigurer<>());
}
+ /**
+ * Allows configuring exception handling. This is automatically applied when using
+ * {@link WebSecurityConfigurerAdapter}.
+ *
+ *
Example Custom Configuration
+ *
+ * The following customization will ensure that users who are denied access are forwarded
+ * to the page "/errors/access-denied".
+ *
+ *
+ * @Configuration
+ * @EnableWebSecurity
+ * public class ExceptionHandlingSecurityConfig extends WebSecurityConfigurerAdapter {
+ *
+ * @Override
+ * protected void configure(HttpSecurity http) throws Exception {
+ * http
+ * .authorizeRequests()
+ * .antMatchers("/**").hasRole("USER")
+ * .and()
+ * // sample exception handling customization
+ * .exceptionHandling(exceptionHandling ->
+ * exceptionHandling
+ * .accessDeniedPage("/errors/access-denied")
+ * );
+ * }
+ * }
+ *
+ *
+ * @param exceptionHandlingCustomizer the {@link Customizer} to provide more options for
+ * the {@link ExceptionHandlingConfigurer}
+ * @return the {@link HttpSecurity} for further customizations
+ * @throws Exception
+ */
+ public HttpSecurity exceptionHandling(Customizer> exceptionHandlingCustomizer) throws Exception {
+ exceptionHandlingCustomizer.customize(getOrApply(new ExceptionHandlingConfigurer<>()));
+ return HttpSecurity.this;
+ }
+
/**
* Sets up management of the {@link SecurityContext} on the
* {@link SecurityContextHolder} between {@link HttpServletRequest}'s. This is
diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerAccessDeniedHandlerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerAccessDeniedHandlerTests.java
index aad85a825b..b2f92d4247 100644
--- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerAccessDeniedHandlerTests.java
+++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ExceptionHandlingConfigurerAccessDeniedHandlerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -86,6 +86,47 @@ public class ExceptionHandlingConfigurerAccessDeniedHandlerTests {
}
}
+ @Test
+ @WithMockUser(roles = "ANYTHING")
+ public void getWhenAccessDeniedOverriddenInLambdaThenCustomizesResponseByRequest()
+ throws Exception {
+ this.spring.register(RequestMatcherBasedAccessDeniedHandlerInLambdaConfig.class).autowire();
+
+ this.mvc.perform(get("/hello"))
+ .andExpect(status().isIAmATeapot());
+
+ this.mvc.perform(get("/goodbye"))
+ .andExpect(status().isForbidden());
+ }
+
+ @EnableWebSecurity
+ static class RequestMatcherBasedAccessDeniedHandlerInLambdaConfig extends WebSecurityConfigurerAdapter {
+ AccessDeniedHandler teapotDeniedHandler =
+ (request, response, exception) ->
+ response.setStatus(HttpStatus.I_AM_A_TEAPOT.value());
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .authorizeRequests()
+ .anyRequest().denyAll()
+ .and()
+ .exceptionHandling(exceptionHandling ->
+ exceptionHandling
+ .defaultAccessDeniedHandlerFor(
+ this.teapotDeniedHandler,
+ new AntPathRequestMatcher("/hello/**")
+ )
+ .defaultAccessDeniedHandlerFor(
+ new AccessDeniedHandlerImpl(),
+ AnyRequestMatcher.INSTANCE
+ )
+ );
+ // @formatter:on
+ }
+ }
+
@Test
@WithMockUser(roles = "ANYTHING")
public void getWhenAccessDeniedOverriddenByOnlyOneHandlerThenAllRequestsUseThatHandler()
diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.java
index eb4763bd55..b6ddacb237 100644
--- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.java
+++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceHttpServerAccessDeniedHandlerTests.java
@@ -83,6 +83,31 @@ public class NamespaceHttpServerAccessDeniedHandlerTests {
return new UsernamePasswordAuthenticationToken("user", null, AuthorityUtils.NO_AUTHORITIES);
}
+ @Test
+ public void requestWhenCustomAccessDeniedPageInLambdaThenForwardedToCustomPage() throws Exception {
+ this.spring.register(AccessDeniedPageInLambdaConfig.class).autowire();
+
+ this.mvc.perform(get("/")
+ .with(authentication(user())))
+ .andExpect(status().isForbidden())
+ .andExpect(forwardedUrl("/AccessDeniedPageConfig"));
+ }
+
+ @EnableWebSecurity
+ static class AccessDeniedPageInLambdaConfig extends WebSecurityConfigurerAdapter {
+ protected void configure(HttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .authorizeRequests()
+ .anyRequest().denyAll()
+ .and()
+ .exceptionHandling(exceptionHandling ->
+ exceptionHandling.accessDeniedPage("/AccessDeniedPageConfig")
+ );
+ // @formatter:on
+ }
+ }
+
@Test
public void requestWhenCustomAccessDeniedHandlerThenBehaviorMatchesNamespace() throws Exception {
this.spring.register(AccessDeniedHandlerRefConfig.class).autowire();
@@ -109,6 +134,39 @@ public class NamespaceHttpServerAccessDeniedHandlerTests {
}
}
+ @Test
+ public void requestWhenCustomAccessDeniedHandlerInLambdaThenBehaviorMatchesNamespace() throws Exception {
+ this.spring.register(AccessDeniedHandlerRefInLambdaConfig.class).autowire();
+
+ this.mvc.perform(get("/")
+ .with(authentication(user())));
+
+ verify(AccessDeniedHandlerRefInLambdaConfig.accessDeniedHandler)
+ .handle(any(HttpServletRequest.class), any(HttpServletResponse.class), any(AccessDeniedException.class));
+ }
+
+ @EnableWebSecurity
+ static class AccessDeniedHandlerRefInLambdaConfig extends WebSecurityConfigurerAdapter {
+ static AccessDeniedHandler accessDeniedHandler = mock(AccessDeniedHandler.class);
+
+ protected void configure(HttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .authorizeRequests()
+ .anyRequest().denyAll()
+ .and()
+ .exceptionHandling(exceptionHandling ->
+ exceptionHandling.accessDeniedHandler(accessDeniedHandler())
+ );
+ // @formatter:on
+ }
+
+ @Bean
+ AccessDeniedHandler accessDeniedHandler() {
+ return accessDeniedHandler;
+ }
+ }
+
private T verifyBean(Class beanClass) {
return verify(this.spring.getContext().getBean(beanClass));
}