Ver código fonte

Migrate CsrfConfigurerTests groovy->java

Issue: gh-4939
Eleftheria Stein 6 anos atrás
pai
commit
be651d9d16

+ 0 - 513
config/src/test/groovy/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.groovy

@@ -1,513 +0,0 @@
-/*
- * Copyright 2002-2013 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.
- * You may obtain a copy of the License at
- *
- *      https://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.config.annotation.web.configurers
-
-import org.springframework.context.annotation.Bean
-import org.springframework.context.annotation.Configuration
-import org.springframework.security.core.userdetails.PasswordEncodedUser
-import org.springframework.security.web.firewall.StrictHttpFirewall
-
-import javax.servlet.http.HttpServletResponse
-
-import spock.lang.Unroll
-
-import org.springframework.mock.web.MockHttpServletRequest
-import org.springframework.mock.web.MockHttpServletResponse
-import org.springframework.security.config.annotation.BaseSpringSpec
-import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
-import org.springframework.security.config.annotation.web.builders.HttpSecurity
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
-import org.springframework.security.web.access.AccessDeniedHandler
-import org.springframework.security.web.csrf.CsrfFilter
-import org.springframework.security.web.csrf.CsrfTokenRepository
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher
-import org.springframework.security.web.util.matcher.RequestMatcher
-import org.springframework.web.servlet.support.RequestDataValueProcessor
-
-/**
- *
- * @author Rob Winch
- */
-class CsrfConfigurerTests extends BaseSpringSpec {
-
-	@Unroll
-	def "csrf applied by default"() {
-		setup:
-		loadConfig(CsrfAppliedDefaultConfig, AllowHttpMethodsFirewallConfig)
-		request.method = httpMethod
-		clearCsrfToken()
-		when:
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-		response.status == httpStatus
-		where:
-		httpMethod | httpStatus
-		'POST'     | HttpServletResponse.SC_FORBIDDEN
-		'PUT'      | HttpServletResponse.SC_FORBIDDEN
-		'PATCH'    | HttpServletResponse.SC_FORBIDDEN
-		'DELETE'   | HttpServletResponse.SC_FORBIDDEN
-		'INVALID'  | HttpServletResponse.SC_FORBIDDEN
-		'GET'      | HttpServletResponse.SC_OK
-		'HEAD'     | HttpServletResponse.SC_OK
-		'TRACE'    | HttpServletResponse.SC_OK
-		'OPTIONS'  | HttpServletResponse.SC_OK
-	}
-
-	def "csrf default creates CsrfRequestDataValueProcessor"() {
-		when:
-		loadConfig(CsrfAppliedDefaultConfig, AllowHttpMethodsFirewallConfig)
-		then:
-		context.getBean(RequestDataValueProcessor)
-	}
-
-	@Configuration
-	static class AllowHttpMethodsFirewallConfig {
-		@Bean
-		StrictHttpFirewall strictHttpFirewall() {
-			StrictHttpFirewall result = new StrictHttpFirewall();
-			result.setAllowedHttpMethods(StrictHttpFirewall.ALLOW_ANY_HTTP_METHOD);
-			return result;
-		}
-	}
-
-	@EnableWebSecurity
-	static class CsrfAppliedDefaultConfig extends WebSecurityConfigurerAdapter {
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-		}
-	}
-
-	def "csrf disable"() {
-		setup:
-		loadConfig(DisableCsrfConfig)
-		request.method = "POST"
-		clearCsrfToken()
-		when:
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-		!findFilter(CsrfFilter)
-		response.status == HttpServletResponse.SC_OK
-	}
-
-	@EnableWebSecurity
-	static class DisableCsrfConfig extends WebSecurityConfigurerAdapter {
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-					.csrf().disable()
-		}
-	}
-
-	def "SEC-2498: Disable CSRF enables RequestCache for any method"() {
-		setup:
-		loadConfig(DisableCsrfEnablesRequestCacheConfig)
-		request.requestURI = '/tosave'
-		request.method = "POST"
-		clearCsrfToken()
-		when:
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-		response.redirectedUrl
-		when:
-		super.setupWeb(request.session)
-		request.method = "POST"
-		request.servletPath = '/login'
-		request.parameters['username'] = ['user'] as String[]
-		request.parameters['password'] = ['password'] as String[]
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-		response.redirectedUrl == 'http://localhost/tosave'
-	}
-
-	@EnableWebSecurity
-	static class DisableCsrfEnablesRequestCacheConfig extends WebSecurityConfigurerAdapter {
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-					.authorizeRequests()
-					.anyRequest().authenticated()
-					.and()
-					.formLogin().and()
-					.csrf().disable()
-		}
-		@Override
-		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
-			auth
-				.inMemoryAuthentication()
-					.withUser(PasswordEncodedUser.user());
-		}
-	}
-
-	def "SEC-2422: csrf expire CSRF token and session-management invalid-session-url"() {
-		setup:
-		loadConfig(InvalidSessionUrlConfig)
-		request.session.clearAttributes()
-		request.setParameter("_csrf","abc")
-		request.method = "POST"
-		when: "No existing expected CsrfToken (session times out) and a POST"
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then: "sent to the session timeout page page"
-		response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
-		response.redirectedUrl == "/error/sessionError"
-		when: "Existing expected CsrfToken and a POST (invalid token provided)"
-		response = new MockHttpServletResponse()
-		request = new MockHttpServletRequest(session: request.session, method:'POST')
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then: "Access Denied occurs"
-		response.status == HttpServletResponse.SC_FORBIDDEN
-	}
-
-	@EnableWebSecurity
-	static class InvalidSessionUrlConfig extends WebSecurityConfigurerAdapter {
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-					.csrf().and()
-					.sessionManagement()
-					.invalidSessionUrl("/error/sessionError")
-		}
-	}
-
-	def "csrf requireCsrfProtectionMatcher"() {
-		setup:
-		RequireCsrfProtectionMatcherConfig.matcher = Mock(RequestMatcher)
-		RequireCsrfProtectionMatcherConfig.matcher.matches(_) >>> [false, true]
-		loadConfig(RequireCsrfProtectionMatcherConfig)
-		clearCsrfToken()
-		when:
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-		response.status == HttpServletResponse.SC_OK
-		when:
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-		response.status == HttpServletResponse.SC_FORBIDDEN
-	}
-
-	@EnableWebSecurity
-	static class RequireCsrfProtectionMatcherConfig extends WebSecurityConfigurerAdapter {
-		static RequestMatcher matcher
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-					.csrf()
-					.requireCsrfProtectionMatcher(matcher)
-		}
-	}
-
-	def "csrf csrfTokenRepository"() {
-		setup:
-		CsrfTokenRepositoryConfig.repo = Mock(CsrfTokenRepository)
-		loadConfig(CsrfTokenRepositoryConfig)
-		clearCsrfToken()
-		when:
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-		1 * CsrfTokenRepositoryConfig.repo.loadToken(_) >> csrfToken
-		response.status == HttpServletResponse.SC_OK
-	}
-
-	def "csrf clears on logout"() {
-		setup:
-		CsrfTokenRepositoryConfig.repo = Mock(CsrfTokenRepository)
-		1 * CsrfTokenRepositoryConfig.repo.loadToken(_) >> csrfToken
-		loadConfig(CsrfTokenRepositoryConfig)
-		login()
-		request.method = "POST"
-		request.servletPath = "/logout"
-		when:
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-		1 *  CsrfTokenRepositoryConfig.repo.saveToken(null, _, _)
-	}
-
-	def "csrf clears on login"() {
-		setup:
-		CsrfTokenRepositoryConfig.repo = Mock(CsrfTokenRepository)
-		(1.._) * CsrfTokenRepositoryConfig.repo.loadToken(_) >> csrfToken
-		(1.._) * CsrfTokenRepositoryConfig.repo.generateToken(_) >> csrfToken
-		loadConfig(CsrfTokenRepositoryConfig)
-		request.method = "POST"
-		request.getSession()
-		request.servletPath = "/login"
-		request.setParameter("username", "user")
-		request.setParameter("password", "password")
-		when:
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-		response.redirectedUrl == "/"
-		(1.._) *  CsrfTokenRepositoryConfig.repo.saveToken(null, _, _)
-	}
-
-	@EnableWebSecurity
-	static class CsrfTokenRepositoryConfig extends WebSecurityConfigurerAdapter {
-		static CsrfTokenRepository repo
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-					.formLogin()
-					.and()
-					.csrf()
-					.csrfTokenRepository(repo)
-		}
-		@Override
-		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
-			auth
-				.inMemoryAuthentication()
-					.withUser(PasswordEncodedUser.user());
-		}
-	}
-
-	def "csrf access denied handler"() {
-		setup:
-		AccessDeniedHandlerConfig.deniedHandler = Mock(AccessDeniedHandler)
-		1 * AccessDeniedHandlerConfig.deniedHandler.handle(_, _, _)
-		loadConfig(AccessDeniedHandlerConfig)
-		clearCsrfToken()
-		request.method = "POST"
-		when:
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-		response.status == HttpServletResponse.SC_OK
-	}
-
-	@EnableWebSecurity
-	static class AccessDeniedHandlerConfig extends WebSecurityConfigurerAdapter {
-		static AccessDeniedHandler deniedHandler
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-					.exceptionHandling()
-					.accessDeniedHandler(deniedHandler)
-		}
-	}
-
-	def "formLogin requires CSRF token"() {
-		setup:
-		loadConfig(FormLoginConfig)
-		clearCsrfToken()
-		request.setParameter("username", "user")
-		request.setParameter("password", "password")
-		request.servletPath = "/login"
-		request.method = "POST"
-		when:
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-		response.status == HttpServletResponse.SC_FORBIDDEN
-		currentAuthentication == null
-	}
-
-	@EnableWebSecurity
-	static class FormLoginConfig extends WebSecurityConfigurerAdapter {
-		static AccessDeniedHandler deniedHandler
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-					.formLogin()
-		}
-	}
-
-	def "logout requires CSRF token"() {
-		setup:
-		loadConfig(LogoutConfig)
-		clearCsrfToken()
-		login()
-		request.servletPath = "/logout"
-		request.method = "POST"
-		when:
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then: "logout is not allowed and user is still authenticated"
-		response.status == HttpServletResponse.SC_FORBIDDEN
-		currentAuthentication != null
-	}
-
-	def "SEC-2543: CSRF means logout requires POST"() {
-		setup:
-		loadConfig(LogoutConfig)
-		login()
-		request.servletPath = "/logout"
-		request.method = "GET"
-		when:
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then: "logout with GET is not performed"
-		currentAuthentication != null
-	}
-
-	@EnableWebSecurity
-	static class LogoutConfig extends WebSecurityConfigurerAdapter {
-		static AccessDeniedHandler deniedHandler
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-					.formLogin()
-		}
-	}
-
-	def "CSRF can explicitly enable GET for logout"() {
-		setup:
-		loadConfig(LogoutAllowsGetConfig)
-		login()
-		request.servletPath = "/logout"
-		request.method = "GET"
-		when:
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then: "logout with GET is not performed"
-		currentAuthentication == null
-	}
-
-	@EnableWebSecurity
-	static class LogoutAllowsGetConfig extends WebSecurityConfigurerAdapter {
-		static AccessDeniedHandler deniedHandler
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-					.formLogin().and()
-					.logout()
-					.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
-		}
-	}
-
-	def "csrf disables POST requests from RequestCache"() {
-		setup:
-		CsrfDisablesPostRequestFromRequestCacheConfig.repo = Mock(CsrfTokenRepository)
-		(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.generateToken(_) >> csrfToken
-		loadConfig(CsrfDisablesPostRequestFromRequestCacheConfig)
-		request.servletPath = "/some-url"
-		request.requestURI = "/some-url"
-		request.method = "POST"
-		when: "CSRF passes and our session times out"
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then: "sent to the login page"
-		(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.loadToken(_) >> csrfToken
-		response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
-		response.redirectedUrl == "http://localhost/login"
-		when: "authenticate successfully"
-		super.setupWeb(request.session)
-		request.servletPath = "/login"
-		request.setParameter("username","user")
-		request.setParameter("password","password")
-		request.method = "POST"
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then: "sent to default success because we don't want csrf attempts made prior to authentication to pass"
-		(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.loadToken(_) >> csrfToken
-		response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
-		response.redirectedUrl == "/"
-	}
-
-	def "csrf enables GET requests with RequestCache"() {
-		setup:
-		CsrfDisablesPostRequestFromRequestCacheConfig.repo = Mock(CsrfTokenRepository)
-		(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.generateToken(_) >> csrfToken
-		loadConfig(CsrfDisablesPostRequestFromRequestCacheConfig)
-		request.servletPath = "/some-url"
-		request.requestURI = "/some-url"
-		request.method = "GET"
-		when: "CSRF passes and our session times out"
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then: "sent to the login page"
-		(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.loadToken(_) >> csrfToken
-		response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
-		response.redirectedUrl == "http://localhost/login"
-		when: "authenticate successfully"
-		super.setupWeb(request.session)
-		request.servletPath = "/login"
-		request.setParameter("username","user")
-		request.setParameter("password","password")
-		request.method = "POST"
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then: "sent to original URL since it was a GET"
-		(1.._) * CsrfDisablesPostRequestFromRequestCacheConfig.repo.loadToken(_) >> csrfToken
-		response.status == HttpServletResponse.SC_MOVED_TEMPORARILY
-		response.redirectedUrl == "http://localhost/some-url"
-	}
-
-	@EnableWebSecurity
-	static class CsrfDisablesPostRequestFromRequestCacheConfig extends WebSecurityConfigurerAdapter {
-		static CsrfTokenRepository repo
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			http
-					.authorizeRequests()
-					.anyRequest().authenticated()
-					.and()
-					.formLogin()
-					.and()
-					.csrf()
-					.csrfTokenRepository(repo)
-		}
-		@Override
-		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
-			auth
-				.inMemoryAuthentication()
-					.withUser(PasswordEncodedUser.user());
-		}
-	}
-
-	def 'SEC-2749: requireCsrfProtectionMatcher null'() {
-		when:
-		new CsrfConfigurer<>().requireCsrfProtectionMatcher(null)
-		then:
-		thrown(IllegalArgumentException)
-	}
-
-	def 'default does not create session'() {
-		setup:
-		request = new MockHttpServletRequest(method:'GET')
-		loadConfig(DefaultDoesNotCreateSession)
-		when:
-		springSecurityFilterChain.doFilter(request,response,chain)
-		then:
-		request.getSession(false) == null
-	}
-
-	@EnableWebSecurity(debug=true)
-	static class DefaultDoesNotCreateSession extends WebSecurityConfigurerAdapter {
-
-		@Override
-		protected void configure(HttpSecurity http) throws Exception {
-			// @formatter:off
-			http
-					.authorizeRequests()
-					.anyRequest().permitAll()
-					.and()
-					.formLogin().and()
-					.httpBasic();
-			// @formatter:on
-		}
-
-		@Override
-		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
-			auth
-				.inMemoryAuthentication()
-					.withUser(PasswordEncodedUser.user());
-		}
-	}
-
-	def clearCsrfToken() {
-		request.removeAllParameters()
-	}
-}

+ 616 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java

@@ -0,0 +1,616 @@
+/*
+ * 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.
+ * You may obtain a copy of the License at
+ *
+ *      https://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.config.annotation.web.configurers;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.mock.web.MockHttpSession;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.test.SpringTestRule;
+import org.springframework.security.core.userdetails.PasswordEncodedUser;
+import org.springframework.security.web.access.AccessDeniedHandler;
+import org.springframework.security.web.csrf.CsrfTokenRepository;
+import org.springframework.security.web.csrf.DefaultCsrfToken;
+import org.springframework.security.web.firewall.StrictHttpFirewall;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.support.RequestDataValueProcessor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.net.URI;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
+import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
+import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
+import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.head;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+/**
+ * Tests for {@link CsrfConfigurer}
+ *
+ * @author Rob Winch
+ * @author Eleftheria Stein
+ */
+public class CsrfConfigurerTests {
+	@Rule
+	public final SpringTestRule spring = new SpringTestRule();
+
+	@Autowired
+	MockMvc mvc;
+
+	@Test
+	public void postWhenWebSecurityEnabledThenRespondsWithForbidden() throws Exception {
+		this.spring.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)
+				.autowire();
+
+		this.mvc.perform(post("/"))
+				.andExpect(status().isForbidden());
+	}
+
+	@Test
+	public void putWhenWebSecurityEnabledThenRespondsWithForbidden() throws Exception {
+		this.spring.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)
+				.autowire();
+
+		this.mvc.perform(put("/"))
+				.andExpect(status().isForbidden());
+	}
+
+	@Test
+	public void patchWhenWebSecurityEnabledThenRespondsWithForbidden() throws Exception {
+		this.spring.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)
+				.autowire();
+
+		this.mvc.perform(patch("/"))
+				.andExpect(status().isForbidden());
+	}
+
+	@Test
+	public void deleteWhenWebSecurityEnabledThenRespondsWithForbidden() throws Exception {
+		this.spring.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)
+				.autowire();
+
+		this.mvc.perform(delete("/"))
+				.andExpect(status().isForbidden());
+	}
+
+	@Test
+	public void invalidWhenWebSecurityEnabledThenRespondsWithForbidden() throws Exception {
+		this.spring.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)
+				.autowire();
+
+		this.mvc.perform(request("INVALID", URI.create("/")))
+				.andExpect(status().isForbidden());
+	}
+
+	@Test
+	public void getWhenWebSecurityEnabledThenRespondsWithOk() throws Exception {
+		this.spring.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)
+				.autowire();
+
+		this.mvc.perform(get("/"))
+				.andExpect(status().isOk());
+	}
+
+	@Test
+	public void headWhenWebSecurityEnabledThenRespondsWithOk() throws Exception {
+		this.spring.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)
+				.autowire();
+
+		this.mvc.perform(head("/"))
+				.andExpect(status().isOk());
+	}
+
+	@Test
+	public void traceWhenWebSecurityEnabledThenRespondsWithOk() throws Exception {
+		this.spring.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)
+				.autowire();
+
+		this.mvc.perform(request(HttpMethod.TRACE, "/"))
+				.andExpect(status().isOk());
+	}
+
+	@Test
+	public void optionsWhenWebSecurityEnabledThenRespondsWithOk() throws Exception {
+		this.spring.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class, BasicController.class)
+				.autowire();
+
+		this.mvc.perform(options("/"))
+				.andExpect(status().isOk());
+	}
+
+	@Test
+	public void enableWebSecurityWhenDefaultConfigurationThenCreatesRequestDataValueProcessor() {
+		this.spring.register(CsrfAppliedDefaultConfig.class, AllowHttpMethodsFirewallConfig.class).autowire();
+
+		assertThat(this.spring.getContext().getBean(RequestDataValueProcessor.class)).isNotNull();
+	}
+
+	@Configuration
+	static class AllowHttpMethodsFirewallConfig {
+		@Bean
+		StrictHttpFirewall strictHttpFirewall() {
+			StrictHttpFirewall result = new StrictHttpFirewall();
+			result.setUnsafeAllowAnyHttpMethod(true);
+			return result;
+		}
+	}
+
+	@EnableWebSecurity
+	static class CsrfAppliedDefaultConfig extends WebSecurityConfigurerAdapter {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+		}
+	}
+
+	@Test
+	public void postWhenCsrfDisabledThenRespondsWithOk() throws Exception {
+		this.spring.register(DisableCsrfConfig.class, BasicController.class).autowire();
+
+		this.mvc.perform(post("/"))
+				.andExpect(status().isOk());
+	}
+
+	@EnableWebSecurity
+	static class DisableCsrfConfig extends WebSecurityConfigurerAdapter {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.csrf()
+					.disable();
+			// @formatter:on
+		}
+	}
+
+	// SEC-2498
+	@Test
+	public void loginWhenCsrfDisabledThenRedirectsToPreviousPostRequest() throws Exception {
+		this.spring.register(DisableCsrfEnablesRequestCacheConfig.class).autowire();
+
+		MvcResult mvcResult = this.mvc.perform(post("/to-save")).andReturn();
+
+		this.mvc.perform(post("/login")
+				.param("username", "user")
+				.param("password", "password")
+				.session((MockHttpSession) mvcResult.getRequest().getSession()))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("http://localhost/to-save"));
+	}
+
+	@EnableWebSecurity
+	static class DisableCsrfEnablesRequestCacheConfig extends WebSecurityConfigurerAdapter {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeRequests()
+					.anyRequest().authenticated()
+					.and()
+				.formLogin()
+					.and()
+				.csrf()
+					.disable();
+			// @formatter:on
+		}
+
+		@Override
+		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+			// @formatter:off
+			auth
+				.inMemoryAuthentication()
+				.withUser(PasswordEncodedUser.user());
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void loginWhenCsrfEnabledThenDoesNotRedirectToPreviousPostRequest() throws Exception {
+		CsrfDisablesPostRequestFromRequestCacheConfig.REPO = mock(CsrfTokenRepository.class);
+		DefaultCsrfToken csrfToken = new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "token");
+		when(CsrfDisablesPostRequestFromRequestCacheConfig.REPO.loadToken(any())).thenReturn(csrfToken);
+		when(CsrfDisablesPostRequestFromRequestCacheConfig.REPO.generateToken(any())).thenReturn(csrfToken);
+		this.spring.register(CsrfDisablesPostRequestFromRequestCacheConfig.class).autowire();
+
+		MvcResult mvcResult = this.mvc.perform(post("/some-url"))
+				.andReturn();
+		this.mvc.perform(post("/login")
+				.param("username", "user")
+				.param("password", "password")
+				.with(csrf())
+				.session((MockHttpSession) mvcResult.getRequest().getSession()))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("/"));
+
+		verify(CsrfDisablesPostRequestFromRequestCacheConfig.REPO, atLeastOnce()).loadToken(any(HttpServletRequest.class));
+	}
+
+	@Test
+	public void loginWhenCsrfEnabledThenRedirectsToPreviousGetRequest() throws Exception {
+		CsrfDisablesPostRequestFromRequestCacheConfig.REPO = mock(CsrfTokenRepository.class);
+		DefaultCsrfToken csrfToken = new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "token");
+		when(CsrfDisablesPostRequestFromRequestCacheConfig.REPO.loadToken(any())).thenReturn(csrfToken);
+		when(CsrfDisablesPostRequestFromRequestCacheConfig.REPO.generateToken(any())).thenReturn(csrfToken);
+		this.spring.register(CsrfDisablesPostRequestFromRequestCacheConfig.class).autowire();
+
+		MvcResult mvcResult = this.mvc.perform(get("/some-url"))
+				.andReturn();
+		this.mvc.perform(post("/login")
+				.param("username", "user")
+				.param("password", "password")
+				.with(csrf())
+				.session((MockHttpSession) mvcResult.getRequest().getSession()))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("http://localhost/some-url"));
+
+		verify(CsrfDisablesPostRequestFromRequestCacheConfig.REPO, atLeastOnce()).loadToken(any(HttpServletRequest.class));
+	}
+
+	@EnableWebSecurity
+	static class CsrfDisablesPostRequestFromRequestCacheConfig extends WebSecurityConfigurerAdapter {
+		static CsrfTokenRepository REPO;
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeRequests()
+					.anyRequest().authenticated()
+					.and()
+				.formLogin()
+					.and()
+				.csrf()
+					.csrfTokenRepository(REPO);
+			// @formatter:on
+		}
+
+		@Override
+		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+			// @formatter:off
+			auth
+				.inMemoryAuthentication()
+					.withUser(PasswordEncodedUser.user());
+			// @formatter:on
+		}
+	}
+
+	// SEC-2422
+	@Test
+	public void postWhenCsrfEnabledAndSessionIsExpiredThenRespondsWithForbidden() throws Exception {
+		this.spring.register(InvalidSessionUrlConfig.class).autowire();
+
+		MvcResult mvcResult = this.mvc.perform(post("/")
+				.param("_csrf", "abc"))
+				.andExpect(status().isFound())
+				.andExpect(redirectedUrl("/error/sessionError"))
+				.andReturn();
+
+		this.mvc.perform(post("/")
+				.session((MockHttpSession) mvcResult.getRequest().getSession()))
+				.andExpect(status().isForbidden());
+	}
+
+	@EnableWebSecurity
+	static class InvalidSessionUrlConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.csrf()
+					.and()
+				.sessionManagement()
+					.invalidSessionUrl("/error/sessionError");
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void requireCsrfProtectionMatcherWhenRequestDoesNotMatchThenRespondsWithOk() throws Exception {
+		this.spring.register(RequireCsrfProtectionMatcherConfig.class, BasicController.class).autowire();
+		when(RequireCsrfProtectionMatcherConfig.MATCHER.matches(any()))
+				.thenReturn(false);
+
+		this.mvc.perform(get("/"))
+				.andExpect(status().isOk());
+	}
+
+	@Test
+	public void requireCsrfProtectionMatcherWhenRequestMatchesThenRespondsWithForbidden() throws Exception {
+		RequireCsrfProtectionMatcherConfig.MATCHER = mock(RequestMatcher.class);
+		when(RequireCsrfProtectionMatcherConfig.MATCHER.matches(any())).thenReturn(true);
+		this.spring.register(RequireCsrfProtectionMatcherConfig.class, BasicController.class).autowire();
+
+		this.mvc.perform(get("/"))
+				.andExpect(status().isForbidden());
+	}
+
+	@EnableWebSecurity
+	static class RequireCsrfProtectionMatcherConfig extends WebSecurityConfigurerAdapter {
+		static RequestMatcher MATCHER;
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.csrf()
+					.requireCsrfProtectionMatcher(MATCHER);
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void getWhenCustomCsrfTokenRepositoryThenRepositoryIsUsed() throws Exception {
+		CsrfTokenRepositoryConfig.REPO = mock(CsrfTokenRepository.class);
+		when(CsrfTokenRepositoryConfig.REPO.loadToken(any()))
+				.thenReturn(new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "token"));
+		this.spring.register(CsrfTokenRepositoryConfig.class, BasicController.class).autowire();
+
+		this.mvc.perform(get("/"))
+				.andExpect(status().isOk());
+		verify(CsrfTokenRepositoryConfig.REPO).loadToken(any(HttpServletRequest.class));
+	}
+
+	@Test
+	public void logoutWhenCustomCsrfTokenRepositoryThenCsrfTokenIsCleared() throws Exception {
+		CsrfTokenRepositoryConfig.REPO = mock(CsrfTokenRepository.class);
+		this.spring.register(CsrfTokenRepositoryConfig.class, BasicController.class).autowire();
+
+		this.mvc.perform(post("/logout")
+				.with(csrf())
+				.with(user("user")));
+
+		verify(CsrfTokenRepositoryConfig.REPO)
+				.saveToken(isNull(), any(HttpServletRequest.class), any(HttpServletResponse.class));
+	}
+
+	@Test
+	public void loginWhenCustomCsrfTokenRepositoryThenCsrfTokenIsCleared() throws Exception {
+		CsrfTokenRepositoryConfig.REPO = mock(CsrfTokenRepository.class);
+		DefaultCsrfToken csrfToken = new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "token");
+		when(CsrfTokenRepositoryConfig.REPO.loadToken(any())).thenReturn(csrfToken);
+		when(CsrfTokenRepositoryConfig.REPO.generateToken(any())).thenReturn(csrfToken);
+		this.spring.register(CsrfTokenRepositoryConfig.class, BasicController.class).autowire();
+
+		this.mvc.perform(post("/login")
+				.with(csrf())
+				.param("username", "user")
+				.param("password", "password"))
+				.andExpect(redirectedUrl("/"));
+
+		verify(CsrfTokenRepositoryConfig.REPO)
+				.saveToken(isNull(), any(HttpServletRequest.class), any(HttpServletResponse.class));
+	}
+
+	@EnableWebSecurity
+	static class CsrfTokenRepositoryConfig extends WebSecurityConfigurerAdapter {
+		static CsrfTokenRepository REPO;
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.formLogin()
+					.and()
+				.csrf()
+					.csrfTokenRepository(REPO);
+			// @formatter:on
+		}
+
+		@Override
+		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+			// @formatter:off
+			auth
+				.inMemoryAuthentication()
+					.withUser(PasswordEncodedUser.user());
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void getWhenCustomAccessDeniedHandlerThenHandlerIsUsed() throws Exception {
+		AccessDeniedHandlerConfig.DENIED_HANDLER = mock(AccessDeniedHandler.class);
+		this.spring.register(AccessDeniedHandlerConfig.class, BasicController.class).autowire();
+
+		this.mvc.perform(post("/"))
+				.andExpect(status().isOk());
+
+		verify(AccessDeniedHandlerConfig.DENIED_HANDLER)
+				.handle(any(HttpServletRequest.class), any(HttpServletResponse.class), any());
+	}
+
+	@EnableWebSecurity
+	static class AccessDeniedHandlerConfig extends WebSecurityConfigurerAdapter {
+		static AccessDeniedHandler DENIED_HANDLER;
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.exceptionHandling()
+					.accessDeniedHandler(DENIED_HANDLER);
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void loginWhenNoCsrfTokenThenRespondsWithForbidden() throws Exception {
+		this.spring.register(FormLoginConfig.class).autowire();
+
+		this.mvc.perform(post("/login")
+				.param("username", "user")
+				.param("password", "password"))
+				.andExpect(status().isForbidden())
+				.andExpect(unauthenticated());
+	}
+
+	@Test
+	public void logoutWhenNoCsrfTokenThenRespondsWithForbidden() throws Exception {
+		this.spring.register(FormLoginConfig.class).autowire();
+
+		this.mvc.perform(post("/logout")
+				.with(user("username")))
+				.andExpect(status().isForbidden())
+				.andExpect(authenticated());
+	}
+
+	// SEC-2543
+	@Test
+	public void logoutWhenCsrfEnabledAndGetRequestThenDoesNotLogout() throws Exception {
+		this.spring.register(FormLoginConfig.class).autowire();
+
+		this.mvc.perform(get("/logout")
+				.with(user("username")))
+				.andExpect(authenticated());
+	}
+
+	@EnableWebSecurity
+	static class FormLoginConfig extends WebSecurityConfigurerAdapter {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.formLogin();
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void logoutWhenGetRequestAndGetEnabledForLogoutThenLogsOut() throws Exception {
+		this.spring.register(LogoutAllowsGetConfig.class).autowire();
+
+		this.mvc.perform(get("/logout")
+				.with(user("username")))
+				.andExpect(unauthenticated());
+	}
+
+	@EnableWebSecurity
+	static class LogoutAllowsGetConfig extends WebSecurityConfigurerAdapter {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.formLogin()
+					.and()
+				.logout()
+					.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
+			// @formatter:on
+		}
+	}
+
+	// SEC-2749
+	@Test
+	public void configureWhenRequireCsrfProtectionMatcherNullThenException() {
+		assertThatThrownBy(() -> this.spring.register(NullRequireCsrfProtectionMatcherConfig.class).autowire())
+				.isInstanceOf(BeanCreationException.class)
+				.hasRootCauseInstanceOf(IllegalArgumentException.class);
+	}
+
+	@EnableWebSecurity
+	static class NullRequireCsrfProtectionMatcherConfig extends WebSecurityConfigurerAdapter {
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.csrf()
+					.requireCsrfProtectionMatcher(null);
+			// @formatter:on
+		}
+	}
+
+	@Test
+	public void getWhenDefaultCsrfTokenRepositoryThenDoesNotCreateSession() throws Exception {
+		this.spring.register(DefaultDoesNotCreateSession.class).autowire();
+
+		MvcResult mvcResult = this.mvc.perform(get("/"))
+				.andReturn();
+
+		assertThat(mvcResult.getRequest().getSession(false)).isNull();
+	}
+
+	@EnableWebSecurity
+	static class DefaultDoesNotCreateSession extends WebSecurityConfigurerAdapter {
+
+		@Override
+		protected void configure(HttpSecurity http) throws Exception {
+			// @formatter:off
+			http
+				.authorizeRequests()
+					.anyRequest().permitAll()
+					.and()
+				.formLogin()
+					.and()
+				.httpBasic();
+			// @formatter:on
+		}
+
+		@Override
+		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+			// @formatter:off
+			auth
+				.inMemoryAuthentication()
+					.withUser(PasswordEncodedUser.user());
+			// @formatter:on
+		}
+	}
+
+	@RestController
+	static class BasicController {
+		@GetMapping("/")
+		public void rootGet() {
+		}
+
+		@PostMapping("/")
+		public void rootPost() {
+		}
+	}
+}