Skip to content

Commit 09e8ae4

Browse files
mvitzrwinch
authored andcommitted
Allow configuration of SessionAuthenticationStrategy for CSRF
Closes gh-5300
1 parent ea54d90 commit 09e8ae4

File tree

2 files changed

+106
-10
lines changed

2 files changed

+106
-10
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurer.java

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
3030
import org.springframework.security.web.access.AccessDeniedHandler;
3131
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
3232
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
33+
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
3334
import org.springframework.security.web.csrf.CsrfAuthenticationStrategy;
3435
import org.springframework.security.web.csrf.CsrfFilter;
3536
import org.springframework.security.web.csrf.CsrfLogoutHandler;
@@ -81,6 +82,7 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
8182
new HttpSessionCsrfTokenRepository());
8283
private RequestMatcher requireCsrfProtectionMatcher = CsrfFilter.DEFAULT_CSRF_MATCHER;
8384
private List<RequestMatcher> ignoredCsrfProtectionMatchers = new ArrayList<>();
85+
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
8486
private final ApplicationContext context;
8587

8688
/**
@@ -179,6 +181,26 @@ public CsrfConfigurer<H> ignoringRequestMatchers(RequestMatcher... requestMatche
179181
.and();
180182
}
181183

184+
/**
185+
* <p>
186+
* Specify the {@link SessionAuthenticationStrategy} to use. The default is a
187+
* {@link CsrfAuthenticationStrategy}.
188+
* </p>
189+
*
190+
* @author Michael Vitz
191+
* @since 5.1
192+
*
193+
* @param sessionAuthenticationStrategy the {@link SessionAuthenticationStrategy} to use
194+
* @return the {@link CsrfConfigurer} for further customizations
195+
*/
196+
public CsrfConfigurer<H> sessionAuthenticationStrategy(
197+
SessionAuthenticationStrategy sessionAuthenticationStrategy) {
198+
Assert.notNull(sessionAuthenticationStrategy,
199+
"sessionAuthenticationStrategy cannot be null");
200+
this.sessionAuthenticationStrategy = sessionAuthenticationStrategy;
201+
return this;
202+
}
203+
182204
@SuppressWarnings("unchecked")
183205
@Override
184206
public void configure(H http) throws Exception {
@@ -200,7 +222,7 @@ public void configure(H http) throws Exception {
200222
.getConfigurer(SessionManagementConfigurer.class);
201223
if (sessionConfigurer != null) {
202224
sessionConfigurer.addSessionAuthenticationStrategy(
203-
new CsrfAuthenticationStrategy(this.csrfTokenRepository));
225+
getSessionAuthenticationStrategy());
204226
}
205227
filter = postProcess(filter);
206228
http.addFilter(filter);
@@ -289,6 +311,23 @@ private AccessDeniedHandler createAccessDeniedHandler(H http) {
289311
return new DelegatingAccessDeniedHandler(handlers, defaultAccessDeniedHandler);
290312
}
291313

314+
/**
315+
* Gets the {@link SessionAuthenticationStrategy} to use. If none was set by the user a
316+
* {@link CsrfAuthenticationStrategy} is created.
317+
*
318+
* @author Michael Vitz
319+
* @since 5.1
320+
*
321+
* @return the {@link SessionAuthenticationStrategy}
322+
*/
323+
private SessionAuthenticationStrategy getSessionAuthenticationStrategy() {
324+
if (sessionAuthenticationStrategy != null) {
325+
return sessionAuthenticationStrategy;
326+
} else {
327+
return new CsrfAuthenticationStrategy(this.csrfTokenRepository);
328+
}
329+
}
330+
292331
/**
293332
* Allows registering {@link RequestMatcher} instances that should be ignored (even if
294333
* the {@link HttpServletRequest} matches the

config/src/test/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurerTests.java

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@
2929
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
3030
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
3131
import org.springframework.security.config.test.SpringTestRule;
32+
import org.springframework.security.core.Authentication;
3233
import org.springframework.security.core.userdetails.PasswordEncodedUser;
3334
import org.springframework.security.web.access.AccessDeniedHandler;
35+
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
3436
import org.springframework.security.web.csrf.CsrfTokenRepository;
3537
import org.springframework.security.web.csrf.DefaultCsrfToken;
3638
import org.springframework.security.web.firewall.StrictHttpFirewall;
@@ -60,14 +62,7 @@
6062
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
6163
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
6264
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
63-
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
64-
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
65-
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.head;
66-
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
67-
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
68-
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
69-
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
70-
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request;
65+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
7166
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
7267
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
7368

@@ -76,6 +71,8 @@
7671
*
7772
* @author Rob Winch
7873
* @author Eleftheria Stein
74+
* @author Michael Vitz
75+
* @author Sam Simmons
7976
*/
8077
public class CsrfConfigurerTests {
8178
@Rule
@@ -684,6 +681,66 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
684681
}
685682
}
686683

684+
@EnableWebSecurity
685+
static class NullAuthenticationStrategy extends WebSecurityConfigurerAdapter {
686+
@Override
687+
protected void configure(HttpSecurity http) throws Exception {
688+
// @formatter:off
689+
http
690+
.csrf()
691+
.sessionAuthenticationStrategy(null);
692+
// @formatter:on
693+
}
694+
}
695+
696+
@Test
697+
public void getWhenNullAuthenticationStrategyThenException() {
698+
assertThatThrownBy(() -> this.spring.register(NullAuthenticationStrategy.class).autowire())
699+
.isInstanceOf(BeanCreationException.class)
700+
.hasRootCauseInstanceOf(IllegalArgumentException.class);
701+
}
702+
703+
@EnableWebSecurity
704+
static class CsrfAuthenticationStrategyConfig extends WebSecurityConfigurerAdapter {
705+
static SessionAuthenticationStrategy STRATEGY;
706+
707+
@Override
708+
protected void configure(HttpSecurity http) throws Exception {
709+
// @formatter:off
710+
http
711+
.formLogin()
712+
.and()
713+
.csrf()
714+
.sessionAuthenticationStrategy(STRATEGY);
715+
// @formatter:on
716+
}
717+
718+
@Override
719+
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
720+
// @formatter:off
721+
auth
722+
.inMemoryAuthentication()
723+
.withUser(PasswordEncodedUser.user());
724+
// @formatter:on
725+
}
726+
}
727+
728+
@Test
729+
public void csrfAuthenticationStrategyConfiguredThenStrategyUsed() throws Exception {
730+
CsrfAuthenticationStrategyConfig.STRATEGY = mock(SessionAuthenticationStrategy.class);
731+
732+
this.spring.register(CsrfAuthenticationStrategyConfig.class).autowire();
733+
734+
this.mvc.perform(post("/login")
735+
.with(csrf())
736+
.param("username", "user")
737+
.param("password", "password"))
738+
.andExpect(redirectedUrl("/"));
739+
740+
verify(CsrfAuthenticationStrategyConfig.STRATEGY, atLeastOnce())
741+
.onAuthentication(any(Authentication.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
742+
}
743+
687744
@RestController
688745
static class BasicController {
689746
@GetMapping("/")

0 commit comments

Comments
 (0)