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 341515f5c3..bad4a7fbb4 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
@@ -771,6 +771,35 @@ public final class HttpSecurity extends
return getOrApply(new CsrfConfigurer<>(context));
}
+ /**
+ * Adds CSRF support. This is activated by default when using
+ * {@link WebSecurityConfigurerAdapter}'s default constructor. You can disable it
+ * using:
+ *
+ *
+ * @Configuration
+ * @EnableWebSecurity
+ * public class CsrfSecurityConfig extends WebSecurityConfigurerAdapter {
+ *
+ * @Override
+ * protected void configure(HttpSecurity http) throws Exception {
+ * http
+ * .csrf(csrf -> csrf.disable());
+ * }
+ * }
+ *
+ *
+ * @param csrfCustomizer the {@link Customizer} to provide more options for
+ * the {@link CsrfConfigurer}
+ * @return the {@link HttpSecurity} for further customizations
+ * @throws Exception
+ */
+ public HttpSecurity csrf(Customizer> csrfCustomizer) throws Exception {
+ ApplicationContext context = getContext();
+ csrfCustomizer.customize(getOrApply(new CsrfConfigurer<>(context)));
+ return HttpSecurity.this;
+ }
+
/**
* Provides logout support. This is automatically applied when using
* {@link WebSecurityConfigurerAdapter}. The default is that accessing the URL
diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerIgnoringRequestMatchersTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerIgnoringRequestMatchersTests.java
index 39af9745cb..56d99f0610 100644
--- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerIgnoringRequestMatchersTests.java
+++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerIgnoringRequestMatchersTests.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.
@@ -75,6 +75,36 @@ public class CsrfConfigurerIgnoringRequestMatchersTests {
}
}
+ @Test
+ public void requestWhenIgnoringRequestMatchersInLambdaThenAugmentedByConfiguredRequestMatcher()
+ throws Exception {
+ this.spring.register(IgnoringRequestInLambdaMatchers.class, BasicController.class).autowire();
+
+ this.mvc.perform(get("/path"))
+ .andExpect(status().isForbidden());
+
+ this.mvc.perform(post("/path"))
+ .andExpect(status().isOk());
+ }
+
+ @EnableWebSecurity
+ static class IgnoringRequestInLambdaMatchers extends WebSecurityConfigurerAdapter {
+ RequestMatcher requestMatcher =
+ request -> HttpMethod.POST.name().equals(request.getMethod());
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .csrf(csrf ->
+ csrf
+ .requireCsrfProtectionMatcher(new AntPathRequestMatcher("/path"))
+ .ignoringRequestMatchers(this.requestMatcher)
+ );
+ // @formatter:on
+ }
+ }
+
@Test
public void requestWhenIgnoringRequestMatcherThenUnionsWithConfiguredIgnoringAntMatchers()
throws Exception {
@@ -107,6 +137,40 @@ public class CsrfConfigurerIgnoringRequestMatchersTests {
}
}
+ @Test
+ public void requestWhenIgnoringRequestMatcherInLambdaThenUnionsWithConfiguredIgnoringAntMatchers()
+ throws Exception {
+
+ this.spring.register(IgnoringPathsAndMatchersInLambdaConfig.class, BasicController.class).autowire();
+
+ this.mvc.perform(put("/csrf"))
+ .andExpect(status().isForbidden());
+
+ this.mvc.perform(post("/csrf"))
+ .andExpect(status().isOk());
+
+ this.mvc.perform(put("/no-csrf"))
+ .andExpect(status().isOk());
+ }
+
+ @EnableWebSecurity
+ static class IgnoringPathsAndMatchersInLambdaConfig extends WebSecurityConfigurerAdapter {
+ RequestMatcher requestMatcher =
+ request -> HttpMethod.POST.name().equals(request.getMethod());
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .csrf(csrf ->
+ csrf
+ .ignoringAntMatchers("/no-csrf")
+ .ignoringRequestMatchers(this.requestMatcher)
+ );
+ // @formatter:on
+ }
+ }
+
@RestController
public static class BasicController {
@RequestMapping("/path")
diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java
index e66fd1a075..be2aad0815 100644
--- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java
+++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java
@@ -210,6 +210,26 @@ public class CsrfConfigurerTests {
}
}
+ @Test
+ public void postWhenCsrfDisabledInLambdaThenRespondsWithOk() throws Exception {
+ this.spring.register(DisableCsrfInLambdaConfig.class, BasicController.class).autowire();
+
+ this.mvc.perform(post("/"))
+ .andExpect(status().isOk());
+ }
+
+ @EnableWebSecurity
+ static class DisableCsrfInLambdaConfig extends WebSecurityConfigurerAdapter {
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .csrf(AbstractHttpConfigurer::disable);
+ // @formatter:on
+ }
+ }
+
// SEC-2498
@Test
public void loginWhenCsrfDisabledThenRedirectsToPreviousPostRequest() throws Exception {
@@ -386,6 +406,40 @@ public class CsrfConfigurerTests {
}
}
+ @Test
+ public void requireCsrfProtectionMatcherInLambdaWhenRequestDoesNotMatchThenRespondsWithOk() throws Exception {
+ RequireCsrfProtectionMatcherInLambdaConfig.MATCHER = mock(RequestMatcher.class);
+ this.spring.register(RequireCsrfProtectionMatcherInLambdaConfig.class, BasicController.class).autowire();
+ when(RequireCsrfProtectionMatcherInLambdaConfig.MATCHER.matches(any()))
+ .thenReturn(false);
+
+ this.mvc.perform(get("/"))
+ .andExpect(status().isOk());
+ }
+
+ @Test
+ public void requireCsrfProtectionMatcherInLambdaWhenRequestMatchesThenRespondsWithForbidden() throws Exception {
+ RequireCsrfProtectionMatcherInLambdaConfig.MATCHER = mock(RequestMatcher.class);
+ when(RequireCsrfProtectionMatcherInLambdaConfig.MATCHER.matches(any())).thenReturn(true);
+ this.spring.register(RequireCsrfProtectionMatcherInLambdaConfig.class, BasicController.class).autowire();
+
+ this.mvc.perform(get("/"))
+ .andExpect(status().isForbidden());
+ }
+
+ @EnableWebSecurity
+ static class RequireCsrfProtectionMatcherInLambdaConfig extends WebSecurityConfigurerAdapter {
+ static RequestMatcher MATCHER;
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .csrf(csrf -> csrf.requireCsrfProtectionMatcher(MATCHER));
+ // @formatter:on
+ }
+ }
+
@Test
public void getWhenCustomCsrfTokenRepositoryThenRepositoryIsUsed() throws Exception {
CsrfTokenRepositoryConfig.REPO = mock(CsrfTokenRepository.class);
@@ -454,6 +508,33 @@ public class CsrfConfigurerTests {
}
}
+ @Test
+ public void getWhenCustomCsrfTokenRepositoryInLambdaThenRepositoryIsUsed() throws Exception {
+ CsrfTokenRepositoryInLambdaConfig.REPO = mock(CsrfTokenRepository.class);
+ when(CsrfTokenRepositoryInLambdaConfig.REPO.loadToken(any()))
+ .thenReturn(new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "token"));
+ this.spring.register(CsrfTokenRepositoryInLambdaConfig.class, BasicController.class).autowire();
+
+ this.mvc.perform(get("/"))
+ .andExpect(status().isOk());
+ verify(CsrfTokenRepositoryInLambdaConfig.REPO).loadToken(any(HttpServletRequest.class));
+ }
+
+ @EnableWebSecurity
+ static class CsrfTokenRepositoryInLambdaConfig extends WebSecurityConfigurerAdapter {
+ static CsrfTokenRepository REPO;
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .formLogin()
+ .and()
+ .csrf(csrf -> csrf.csrfTokenRepository(REPO));
+ // @formatter:on
+ }
+ }
+
@Test
public void getWhenCustomAccessDeniedHandlerThenHandlerIsUsed() throws Exception {
AccessDeniedHandlerConfig.DENIED_HANDLER = mock(AccessDeniedHandler.class);