Skip to content

Commit ed41a60

Browse files
author
Steve Riesenberg
committed
Merge branch '5.8.x'
# Conflicts: # config/src/test/java/org/springframework/security/config/annotation/web/configuration/DeferHttpSessionJavaConfigTests.java # config/src/test/resources/org/springframework/security/config/http/DeferHttpSessionTests-Explicit.xml # web/src/main/java/org/springframework/security/web/csrf/CsrfFilter.java
2 parents e17989d + 86fbb8d commit ed41a60

File tree

17 files changed

+566
-57
lines changed

17 files changed

+566
-57
lines changed

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

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -36,6 +36,8 @@
3636
import org.springframework.security.web.csrf.CsrfFilter;
3737
import org.springframework.security.web.csrf.CsrfLogoutHandler;
3838
import org.springframework.security.web.csrf.CsrfTokenRepository;
39+
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
40+
import org.springframework.security.web.csrf.CsrfTokenRequestResolver;
3941
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
4042
import org.springframework.security.web.csrf.LazyCsrfTokenRepository;
4143
import org.springframework.security.web.csrf.MissingCsrfTokenException;
@@ -89,7 +91,9 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
8991

9092
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
9193

92-
private String csrfRequestAttributeName;
94+
private CsrfTokenRequestAttributeHandler requestAttributeHandler;
95+
96+
private CsrfTokenRequestResolver requestResolver;
9397

9498
private final ApplicationContext context;
9599

@@ -127,12 +131,25 @@ public CsrfConfigurer<H> requireCsrfProtectionMatcher(RequestMatcher requireCsrf
127131
}
128132

129133
/**
130-
* Sets the {@link CsrfFilter#setCsrfRequestAttributeName(String)}
131-
* @param csrfRequestAttributeName the attribute name to set the CsrfToken on.
132-
* @return the {@link CsrfConfigurer} for further customizations.
134+
* Specify a {@link CsrfTokenRequestAttributeHandler} to use for making the
135+
* {@code CsrfToken} available as a request attribute.
136+
* @param requestAttributeHandler the {@link CsrfTokenRequestAttributeHandler} to use
137+
* @return the {@link CsrfConfigurer} for further customizations
138+
*/
139+
public CsrfConfigurer<H> csrfTokenRequestAttributeHandler(
140+
CsrfTokenRequestAttributeHandler requestAttributeHandler) {
141+
this.requestAttributeHandler = requestAttributeHandler;
142+
return this;
143+
}
144+
145+
/**
146+
* Specify a {@link CsrfTokenRequestResolver} to use for resolving the token value
147+
* from the request.
148+
* @param requestResolver the {@link CsrfTokenRequestResolver} to use
149+
* @return the {@link CsrfConfigurer} for further customizations
133150
*/
134-
public CsrfConfigurer<H> csrfRequestAttributeName(String csrfRequestAttributeName) {
135-
this.csrfRequestAttributeName = csrfRequestAttributeName;
151+
public CsrfConfigurer<H> csrfTokenRequestResolver(CsrfTokenRequestResolver requestResolver) {
152+
this.requestResolver = requestResolver;
136153
return this;
137154
}
138155

@@ -214,9 +231,6 @@ public CsrfConfigurer<H> sessionAuthenticationStrategy(
214231
@Override
215232
public void configure(H http) {
216233
CsrfFilter filter = new CsrfFilter(this.csrfTokenRepository);
217-
if (this.csrfRequestAttributeName != null) {
218-
filter.setCsrfRequestAttributeName(this.csrfRequestAttributeName);
219-
}
220234
RequestMatcher requireCsrfProtectionMatcher = getRequireCsrfProtectionMatcher();
221235
if (requireCsrfProtectionMatcher != null) {
222236
filter.setRequireCsrfProtectionMatcher(requireCsrfProtectionMatcher);
@@ -233,6 +247,12 @@ public void configure(H http) {
233247
if (sessionConfigurer != null) {
234248
sessionConfigurer.addSessionAuthenticationStrategy(getSessionAuthenticationStrategy());
235249
}
250+
if (this.requestAttributeHandler != null) {
251+
filter.setRequestAttributeHandler(this.requestAttributeHandler);
252+
}
253+
if (this.requestResolver != null) {
254+
filter.setRequestResolver(this.requestResolver);
255+
}
236256
filter = postProcess(filter);
237257
http.addFilter(filter);
238258
}
@@ -321,7 +341,12 @@ private SessionAuthenticationStrategy getSessionAuthenticationStrategy() {
321341
if (this.sessionAuthenticationStrategy != null) {
322342
return this.sessionAuthenticationStrategy;
323343
}
324-
return new CsrfAuthenticationStrategy(this.csrfTokenRepository);
344+
CsrfAuthenticationStrategy csrfAuthenticationStrategy = new CsrfAuthenticationStrategy(
345+
this.csrfTokenRepository);
346+
if (this.requestAttributeHandler != null) {
347+
csrfAuthenticationStrategy.setRequestAttributeHandler(this.requestAttributeHandler);
348+
}
349+
return csrfAuthenticationStrategy;
325350
}
326351

327352
/**

config/src/main/java/org/springframework/security/config/http/CsrfBeanDefinitionParser.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,20 +66,24 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser {
6666

6767
private static final String DISPATCHER_SERVLET_CLASS_NAME = "org.springframework.web.servlet.DispatcherServlet";
6868

69-
private static final String ATT_REQUEST_ATTRIBUTE_NAME = "request-attribute-name";
70-
7169
private static final String ATT_MATCHER = "request-matcher-ref";
7270

7371
private static final String ATT_REPOSITORY = "token-repository-ref";
7472

75-
private String requestAttributeName;
73+
private static final String ATT_REQUEST_ATTRIBUTE_HANDLER = "request-attribute-handler-ref";
74+
75+
private static final String ATT_REQUEST_RESOLVER = "request-resolver-ref";
7676

7777
private String csrfRepositoryRef;
7878

7979
private BeanDefinition csrfFilter;
8080

8181
private String requestMatcherRef;
8282

83+
private String requestAttributeHandlerRef;
84+
85+
private String requestResolverRef;
86+
8387
@Override
8488
public BeanDefinition parse(Element element, ParserContext pc) {
8589
boolean disabled = element != null && "true".equals(element.getAttribute("disabled"));
@@ -97,8 +101,9 @@ public BeanDefinition parse(Element element, ParserContext pc) {
97101
}
98102
if (element != null) {
99103
this.csrfRepositoryRef = element.getAttribute(ATT_REPOSITORY);
100-
this.requestAttributeName = element.getAttribute(ATT_REQUEST_ATTRIBUTE_NAME);
101104
this.requestMatcherRef = element.getAttribute(ATT_MATCHER);
105+
this.requestAttributeHandlerRef = element.getAttribute(ATT_REQUEST_ATTRIBUTE_HANDLER);
106+
this.requestResolverRef = element.getAttribute(ATT_REQUEST_RESOLVER);
102107
}
103108
if (!StringUtils.hasText(this.csrfRepositoryRef)) {
104109
RootBeanDefinition csrfTokenRepository = new RootBeanDefinition(HttpSessionCsrfTokenRepository.class);
@@ -114,8 +119,11 @@ public BeanDefinition parse(Element element, ParserContext pc) {
114119
if (StringUtils.hasText(this.requestMatcherRef)) {
115120
builder.addPropertyReference("requireCsrfProtectionMatcher", this.requestMatcherRef);
116121
}
117-
if (StringUtils.hasText(this.requestAttributeName)) {
118-
builder.addPropertyValue("csrfRequestAttributeName", this.requestAttributeName);
122+
if (StringUtils.hasText(this.requestAttributeHandlerRef)) {
123+
builder.addPropertyReference("requestAttributeHandler", this.requestAttributeHandlerRef);
124+
}
125+
if (StringUtils.hasText(this.requestResolverRef)) {
126+
builder.addPropertyReference("requestResolver", this.requestResolverRef);
119127
}
120128
this.csrfFilter = builder.getBeanDefinition();
121129
return this.csrfFilter;

config/src/main/resources/org/springframework/security/config/spring-security-5.8.rnc

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,15 +1142,18 @@ csrf =
11421142
csrf-options.attlist &=
11431143
## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled).
11441144
attribute disabled {xsd:boolean}?
1145-
csrf-options.attlist &=
1146-
## The request attribute name the CsrfToken is set on. Default is to set to CsrfToken.parameterName
1147-
attribute request-attribute-name { xsd:token }?
11481145
csrf-options.attlist &=
11491146
## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except "GET", "TRACE", "HEAD", "OPTIONS"
11501147
attribute request-matcher-ref { xsd:token }?
11511148
csrf-options.attlist &=
11521149
## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository.
11531150
attribute token-repository-ref { xsd:token }?
1151+
csrf-options.attlist &=
1152+
## The CsrfTokenRequestAttributeHandler to use. The default is CsrfTokenRequestProcessor.
1153+
attribute request-attribute-handler-ref { xsd:token }?
1154+
csrf-options.attlist &=
1155+
## The CsrfTokenRequestResolver to use. The default is CsrfTokenRequestProcessor.
1156+
attribute request-resolver-ref { xsd:token }?
11541157

11551158
headers =
11561159
## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers.

config/src/main/resources/org/springframework/security/config/spring-security-5.8.xsd

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3235,13 +3235,6 @@
32353235
</xs:documentation>
32363236
</xs:annotation>
32373237
</xs:attribute>
3238-
<xs:attribute name="request-attribute-name" type="xs:token">
3239-
<xs:annotation>
3240-
<xs:documentation>The request attribute name the CsrfToken is set on. Default is to set to
3241-
CsrfToken.parameterName
3242-
</xs:documentation>
3243-
</xs:annotation>
3244-
</xs:attribute>
32453238
<xs:attribute name="request-matcher-ref" type="xs:token">
32463239
<xs:annotation>
32473240
<xs:documentation>The RequestMatcher instance to be used to determine if CSRF should be applied. Default is
@@ -3256,6 +3249,18 @@
32563249
</xs:documentation>
32573250
</xs:annotation>
32583251
</xs:attribute>
3252+
<xs:attribute name="request-attribute-handler-ref" type="xs:token">
3253+
<xs:annotation>
3254+
<xs:documentation>The CsrfTokenRequestAttributeHandler to use. The default is CsrfTokenRequestProcessor.
3255+
</xs:documentation>
3256+
</xs:annotation>
3257+
</xs:attribute>
3258+
<xs:attribute name="request-resolver-ref" type="xs:token">
3259+
<xs:annotation>
3260+
<xs:documentation>The CsrfTokenRequestResolver to use. The default is CsrfTokenRequestProcessor.
3261+
</xs:documentation>
3262+
</xs:annotation>
3263+
</xs:attribute>
32593264
</xs:attributeGroup>
32603265
<xs:element name="headers">
32613266
<xs:annotation>

config/src/test/java/org/springframework/security/config/annotation/web/configuration/DeferHttpSessionJavaConfigTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.security.config.test.SpringTestContextExtension;
3333
import org.springframework.security.web.DefaultSecurityFilterChain;
3434
import org.springframework.security.web.FilterChainProxy;
35+
import org.springframework.security.web.csrf.CsrfTokenRequestProcessor;
3536
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
3637
import org.springframework.security.web.csrf.LazyCsrfTokenRepository;
3738

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

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 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.
@@ -29,6 +29,7 @@
2929
import org.springframework.context.annotation.Configuration;
3030
import org.springframework.http.HttpMethod;
3131
import org.springframework.mock.web.MockHttpSession;
32+
import org.springframework.security.config.Customizer;
3233
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
3334
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
3435
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -37,9 +38,12 @@
3738
import org.springframework.security.config.test.SpringTestContextExtension;
3839
import org.springframework.security.core.Authentication;
3940
import org.springframework.security.core.userdetails.PasswordEncodedUser;
41+
import org.springframework.security.web.SecurityFilterChain;
4042
import org.springframework.security.web.access.AccessDeniedHandler;
4143
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
44+
import org.springframework.security.web.csrf.CsrfToken;
4245
import org.springframework.security.web.csrf.CsrfTokenRepository;
46+
import org.springframework.security.web.csrf.CsrfTokenRequestProcessor;
4347
import org.springframework.security.web.csrf.DefaultCsrfToken;
4448
import org.springframework.security.web.firewall.StrictHttpFirewall;
4549
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
@@ -56,12 +60,16 @@
5660

5761
import static org.assertj.core.api.Assertions.assertThat;
5862
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
63+
import static org.hamcrest.Matchers.containsString;
5964
import static org.mockito.ArgumentMatchers.any;
65+
import static org.mockito.ArgumentMatchers.eq;
6066
import static org.mockito.ArgumentMatchers.isNull;
6167
import static org.mockito.BDDMockito.given;
6268
import static org.mockito.Mockito.atLeastOnce;
6369
import static org.mockito.Mockito.mock;
70+
import static org.mockito.Mockito.times;
6471
import static org.mockito.Mockito.verify;
72+
import static org.mockito.Mockito.verifyNoMoreInteractions;
6573
import static org.springframework.security.config.Customizer.withDefaults;
6674
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
6775
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
@@ -75,6 +83,7 @@
7583
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
7684
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
7785
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.request;
86+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
7887
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl;
7988
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
8089

@@ -85,6 +94,7 @@
8594
* @author Eleftheria Stein
8695
* @author Michael Vitz
8796
* @author Sam Simmons
97+
* @author Steve Riesenberg
8898
*/
8999
@ExtendWith(SpringTestContextExtension.class)
90100
public class CsrfConfigurerTests {
@@ -412,6 +422,47 @@ public void csrfAuthenticationStrategyConfiguredThenStrategyUsed() throws Except
412422
any(HttpServletRequest.class), any(HttpServletResponse.class));
413423
}
414424

425+
@Test
426+
public void getLoginWhenCsrfTokenRequestProcessorSetThenRespondsWithNormalCsrfToken() throws Exception {
427+
CsrfTokenRepository csrfTokenRepository = mock(CsrfTokenRepository.class);
428+
CsrfToken csrfToken = new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "token");
429+
given(csrfTokenRepository.generateToken(any(HttpServletRequest.class))).willReturn(csrfToken);
430+
CsrfTokenRequestProcessorConfig.REPO = csrfTokenRepository;
431+
CsrfTokenRequestProcessorConfig.PROCESSOR = new CsrfTokenRequestProcessor();
432+
this.spring.register(CsrfTokenRequestProcessorConfig.class, BasicController.class).autowire();
433+
this.mvc.perform(get("/login")).andExpect(status().isOk())
434+
.andExpect(content().string(containsString(csrfToken.getToken())));
435+
verify(csrfTokenRepository).loadToken(any(HttpServletRequest.class));
436+
verify(csrfTokenRepository).generateToken(any(HttpServletRequest.class));
437+
verify(csrfTokenRepository).saveToken(eq(csrfToken), any(HttpServletRequest.class),
438+
any(HttpServletResponse.class));
439+
verifyNoMoreInteractions(csrfTokenRepository);
440+
}
441+
442+
@Test
443+
public void loginWhenCsrfTokenRequestProcessorSetAndNormalCsrfTokenThenSuccess() throws Exception {
444+
CsrfToken csrfToken = new DefaultCsrfToken("X-CSRF-TOKEN", "_csrf", "token");
445+
CsrfTokenRepository csrfTokenRepository = mock(CsrfTokenRepository.class);
446+
given(csrfTokenRepository.loadToken(any(HttpServletRequest.class))).willReturn(csrfToken);
447+
given(csrfTokenRepository.generateToken(any(HttpServletRequest.class))).willReturn(csrfToken);
448+
CsrfTokenRequestProcessorConfig.REPO = csrfTokenRepository;
449+
CsrfTokenRequestProcessorConfig.PROCESSOR = new CsrfTokenRequestProcessor();
450+
this.spring.register(CsrfTokenRequestProcessorConfig.class, BasicController.class).autowire();
451+
// @formatter:off
452+
MockHttpServletRequestBuilder loginRequest = post("/login")
453+
.header(csrfToken.getHeaderName(), csrfToken.getToken())
454+
.param("username", "user")
455+
.param("password", "password");
456+
// @formatter:on
457+
this.mvc.perform(loginRequest).andExpect(redirectedUrl("/"));
458+
verify(csrfTokenRepository, times(2)).loadToken(any(HttpServletRequest.class));
459+
verify(csrfTokenRepository).saveToken(isNull(), any(HttpServletRequest.class), any(HttpServletResponse.class));
460+
verify(csrfTokenRepository).generateToken(any(HttpServletRequest.class));
461+
verify(csrfTokenRepository).saveToken(eq(csrfToken), any(HttpServletRequest.class),
462+
any(HttpServletResponse.class));
463+
verifyNoMoreInteractions(csrfTokenRepository);
464+
}
465+
415466
@Configuration
416467
static class AllowHttpMethodsFirewallConfig {
417468

@@ -771,6 +822,43 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
771822

772823
}
773824

825+
@Configuration
826+
@EnableWebSecurity
827+
static class CsrfTokenRequestProcessorConfig {
828+
829+
static CsrfTokenRepository REPO;
830+
831+
static CsrfTokenRequestProcessor PROCESSOR;
832+
833+
@Bean
834+
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
835+
// @formatter:off
836+
http
837+
.authorizeHttpRequests((authorize) -> authorize
838+
.anyRequest().authenticated()
839+
)
840+
.formLogin(Customizer.withDefaults())
841+
.csrf((csrf) -> csrf
842+
.csrfTokenRepository(REPO)
843+
.csrfTokenRequestAttributeHandler(PROCESSOR)
844+
.csrfTokenRequestResolver(PROCESSOR)
845+
);
846+
// @formatter:on
847+
848+
return http.build();
849+
}
850+
851+
@Autowired
852+
void configure(AuthenticationManagerBuilder auth) throws Exception {
853+
// @formatter:off
854+
auth
855+
.inMemoryAuthentication()
856+
.withUser(PasswordEncodedUser.user());
857+
// @formatter:on
858+
}
859+
860+
}
861+
774862
@RestController
775863
static class BasicController {
776864

config/src/test/java/org/springframework/security/config/http/CsrfConfigTests.java

Lines changed: 1 addition & 1 deletion
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-2022 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.

config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-WithRequestAttrName.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@
1616
-->
1717

1818
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
19+
xmlns:p="http://www.springframework.org/schema/p"
1920
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2021
xmlns="http://www.springframework.org/schema/security"
2122
xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
2223
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
2324

2425
<http auto-config="true">
25-
<csrf request-attribute-name="csrf-attribute-name"/>
26+
<csrf request-attribute-handler-ref="requestAttributeHandler"/>
2627
</http>
2728

29+
<b:bean id="requestAttributeHandler" class="org.springframework.security.web.csrf.CsrfTokenRequestProcessor"
30+
p:csrfRequestAttributeName="csrf-attribute-name"/>
2831
<b:import resource="CsrfConfigTests-shared-userservice.xml"/>
2932
</b:beans>

0 commit comments

Comments
 (0)