Skip to content

Commit 3c66ef6

Browse files
author
Steve Riesenberg
committed
Change default SecurityContextRepository
Save SecurityContext in request attributes for stateless session management using RequestAttributeSecurityContextRepository. Closes gh-11026
1 parent ccac34b commit 3c66ef6

File tree

17 files changed

+108
-20
lines changed

17 files changed

+108
-20
lines changed

Diff for: config/src/main/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
4848
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
4949
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
50-
import org.springframework.security.web.context.NullSecurityContextRepository;
50+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
5151
import org.springframework.security.web.context.SecurityContextRepository;
5252
import org.springframework.security.web.savedrequest.NullRequestCache;
5353
import org.springframework.security.web.savedrequest.RequestCache;
@@ -341,7 +341,7 @@ public void init(H http) {
341341
boolean stateless = isStateless();
342342
if (securityContextRepository == null) {
343343
if (stateless) {
344-
http.setSharedObject(SecurityContextRepository.class, new NullSecurityContextRepository());
344+
http.setSharedObject(SecurityContextRepository.class, new RequestAttributeSecurityContextRepository());
345345
}
346346
else {
347347
HttpSessionSecurityContextRepository httpSecurityRepository = new HttpSessionSecurityContextRepository();

Diff for: config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
6262
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
6363
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
64-
import org.springframework.security.web.context.NullSecurityContextRepository;
64+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
6565
import org.springframework.security.web.context.SecurityContextHolderFilter;
6666
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
6767
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
@@ -365,7 +365,7 @@ private void createSecurityContextRepository() {
365365
if (!StringUtils.hasText(repoRef)) {
366366
BeanDefinitionBuilder contextRepo;
367367
if (this.sessionPolicy == SessionCreationPolicy.STATELESS) {
368-
contextRepo = BeanDefinitionBuilder.rootBeanDefinition(NullSecurityContextRepository.class);
368+
contextRepo = BeanDefinitionBuilder.rootBeanDefinition(RequestAttributeSecurityContextRepository.class);
369369
}
370370
else {
371371
contextRepo = BeanDefinitionBuilder.rootBeanDefinition(HttpSessionSecurityContextRepository.class);

Diff for: config/src/test/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerTests.java

+50-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 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.
@@ -43,6 +43,7 @@
4343
import org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy;
4444
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
4545
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
46+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
4647
import org.springframework.security.web.context.SecurityContextRepository;
4748
import org.springframework.security.web.savedrequest.RequestCache;
4849
import org.springframework.security.web.session.ConcurrentSessionFilter;
@@ -340,6 +341,22 @@ public void whenDefaultThenEncodeNotInvoked() throws Exception {
340341
this.mvc.perform(get("/")).andExpect(content().string("encoded"));
341342
}
342343

344+
@Test
345+
public void loginWhenSessionCreationPolicyStatelessThenSecurityContextIsAvailableInRequestAttributes()
346+
throws Exception {
347+
this.spring.register(HttpBasicSessionCreationPolicyStatelessConfig.class).autowire();
348+
// @formatter:off
349+
MvcResult mvcResult = this.mvc.perform(get("/").with(httpBasic("user", "password")))
350+
.andExpect(status().isOk())
351+
.andReturn();
352+
// @formatter:on
353+
HttpSession session = mvcResult.getRequest().getSession(false);
354+
assertThat(session).isNull();
355+
SecurityContext securityContext = (SecurityContext) mvcResult.getRequest()
356+
.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME);
357+
assertThat(securityContext).isNotNull();
358+
}
359+
343360
@Configuration
344361
@EnableWebSecurity
345362
static class SessionManagementRequestCacheConfig extends WebSecurityConfigurerAdapter {
@@ -659,6 +676,38 @@ EncodesUrls encodesUrls() {
659676

660677
}
661678

679+
@Configuration
680+
@EnableWebSecurity
681+
static class HttpBasicSessionCreationPolicyStatelessConfig extends WebSecurityConfigurerAdapter {
682+
683+
@Override
684+
protected void configure(HttpSecurity http) throws Exception {
685+
// @formatter:off
686+
http
687+
.sessionManagement((sessionManagement) ->
688+
sessionManagement
689+
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
690+
)
691+
.httpBasic(withDefaults());
692+
// @formatter:on
693+
}
694+
695+
@Override
696+
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
697+
// @formatter:off
698+
auth
699+
.inMemoryAuthentication()
700+
.withUser(PasswordEncodedUser.user());
701+
// @formatter:on
702+
}
703+
704+
@Bean
705+
EncodesUrls encodesUrls() {
706+
return new EncodesUrls();
707+
}
708+
709+
}
710+
662711
@RestController
663712
static class EncodesUrls {
664713

Diff for: oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilter.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
import org.springframework.security.web.AuthenticationEntryPoint;
4242
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
4343
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
44-
import org.springframework.security.web.context.NullSecurityContextRepository;
44+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
4545
import org.springframework.security.web.context.SecurityContextRepository;
4646
import org.springframework.util.Assert;
4747
import org.springframework.web.filter.OncePerRequestFilter;
@@ -80,7 +80,7 @@ public final class BearerTokenAuthenticationFilter extends OncePerRequestFilter
8080

8181
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
8282

83-
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
83+
private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();
8484

8585
/**
8686
* Construct a {@code BearerTokenAuthenticationFilter} using the provided parameter(s)

Diff for: oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/web/authentication/BearerTokenAuthenticationFilterTests.java

+6-1
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.
@@ -44,6 +44,7 @@
4444
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
4545
import org.springframework.security.web.AuthenticationEntryPoint;
4646
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
47+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
4748
import org.springframework.security.web.context.SecurityContextRepository;
4849

4950
import static org.assertj.core.api.Assertions.assertThat;
@@ -105,6 +106,8 @@ public void doFilterWhenBearerTokenPresentThenAuthenticates() throws ServletExce
105106
.forClass(BearerTokenAuthenticationToken.class);
106107
verify(this.authenticationManager).authenticate(captor.capture());
107108
assertThat(captor.getValue().getPrincipal()).isEqualTo("token");
109+
assertThat(this.request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))
110+
.isNotNull();
108111
}
109112

110113
@Test
@@ -138,6 +141,8 @@ public void doFilterWhenUsingAuthenticationManagerResolverThenAuthenticates() th
138141
.forClass(BearerTokenAuthenticationToken.class);
139142
verify(this.authenticationManager).authenticate(captor.capture());
140143
assertThat(captor.getValue().getPrincipal()).isEqualTo("token");
144+
assertThat(this.request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))
145+
.isNotNull();
141146
}
142147

143148
@Test

Diff for: web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
import org.springframework.security.core.context.SecurityContextHolderStrategy;
4444
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
4545
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
46-
import org.springframework.security.web.context.NullSecurityContextRepository;
46+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
4747
import org.springframework.security.web.context.SecurityContextRepository;
4848
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
4949
import org.springframework.security.web.util.matcher.RequestMatcher;
@@ -140,7 +140,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
140140

141141
private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
142142

143-
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
143+
private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();
144144

145145
/**
146146
* @param defaultFilterProcessesUrl the default value for <tt>filterProcessesUrl</tt>.

Diff for: web/src/main/java/org/springframework/security/web/authentication/AuthenticationFilter.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
import org.springframework.security.core.context.SecurityContext;
3434
import org.springframework.security.core.context.SecurityContextHolder;
3535
import org.springframework.security.core.context.SecurityContextHolderStrategy;
36-
import org.springframework.security.web.context.NullSecurityContextRepository;
36+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
3737
import org.springframework.security.web.context.SecurityContextRepository;
3838
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
3939
import org.springframework.security.web.util.matcher.RequestMatcher;
@@ -80,7 +80,7 @@ public class AuthenticationFilter extends OncePerRequestFilter {
8080
private AuthenticationFailureHandler failureHandler = new AuthenticationEntryPointFailureHandler(
8181
new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
8282

83-
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
83+
private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();
8484

8585
private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;
8686

Diff for: web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
4242
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
4343
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
44-
import org.springframework.security.web.context.NullSecurityContextRepository;
44+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
4545
import org.springframework.security.web.context.SecurityContextRepository;
4646
import org.springframework.security.web.util.matcher.RequestMatcher;
4747
import org.springframework.util.Assert;
@@ -110,7 +110,7 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFi
110110

111111
private RequestMatcher requiresAuthenticationRequestMatcher = new PreAuthenticatedProcessingRequestMatcher();
112112

113-
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
113+
private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();
114114

115115
/**
116116
* Check whether all required properties have been set.

Diff for: web/src/main/java/org/springframework/security/web/authentication/rememberme/RememberMeAuthenticationFilter.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
import org.springframework.security.core.context.SecurityContextHolderStrategy;
3838
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
3939
import org.springframework.security.web.authentication.RememberMeServices;
40-
import org.springframework.security.web.context.NullSecurityContextRepository;
40+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
4141
import org.springframework.security.web.context.SecurityContextRepository;
4242
import org.springframework.util.Assert;
4343
import org.springframework.web.filter.GenericFilterBean;
@@ -79,7 +79,7 @@ public class RememberMeAuthenticationFilter extends GenericFilterBean implements
7979

8080
private RememberMeServices rememberMeServices;
8181

82-
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
82+
private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();
8383

8484
public RememberMeAuthenticationFilter(AuthenticationManager authenticationManager,
8585
RememberMeServices rememberMeServices) {

Diff for: web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
import org.springframework.security.web.AuthenticationEntryPoint;
3838
import org.springframework.security.web.authentication.NullRememberMeServices;
3939
import org.springframework.security.web.authentication.RememberMeServices;
40-
import org.springframework.security.web.context.NullSecurityContextRepository;
40+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
4141
import org.springframework.security.web.context.SecurityContextRepository;
4242
import org.springframework.util.Assert;
4343
import org.springframework.web.filter.OncePerRequestFilter;
@@ -109,7 +109,7 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter {
109109

110110
private BasicAuthenticationConverter authenticationConverter = new BasicAuthenticationConverter();
111111

112-
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
112+
private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();
113113

114114
/**
115115
* Creates an instance which will authenticate against the supplied

Diff for: web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
import org.springframework.security.core.userdetails.UsernameNotFoundException;
5050
import org.springframework.security.core.userdetails.cache.NullUserCache;
5151
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
52-
import org.springframework.security.web.context.NullSecurityContextRepository;
52+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
5353
import org.springframework.security.web.context.SecurityContextRepository;
5454
import org.springframework.util.Assert;
5555
import org.springframework.util.StringUtils;
@@ -111,7 +111,7 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
111111

112112
private boolean createAuthenticatedToken = false;
113113

114-
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
114+
private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();
115115

116116
@Override
117117
public void afterPropertiesSet() {

Diff for: web/src/test/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilterTests.java

+9
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServicesTests;
4545
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;
4646
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
47+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
4748
import org.springframework.security.web.context.SecurityContextRepository;
4849
import org.springframework.security.web.firewall.DefaultHttpFirewall;
4950
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@@ -188,6 +189,8 @@ public void testNormalOperationWithDefaultFilterProcessesUrl() throws Exception
188189
assertThat(response.getRedirectedUrl()).isEqualTo("/mycontext/logged_in.jsp");
189190
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();
190191
assertThat(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString()).isEqualTo("test");
192+
assertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))
193+
.isNotNull();
191194
// Should still have the same session
192195
assertThat(request.getSession()).isEqualTo(sessionPreAuth);
193196
}
@@ -215,6 +218,8 @@ public void testNormalOperationWithDefaultFilterProcessesUrlAndAuthenticationMan
215218
assertThat(response.getRedirectedUrl()).isEqualTo("/mycontext/logged_in.jsp");
216219
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();
217220
assertThat(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString()).isEqualTo("test");
221+
assertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))
222+
.isNotNull();
218223
// Should still have the same session
219224
assertThat(request.getSession()).isEqualTo(sessionPreAuth);
220225
}
@@ -244,6 +249,8 @@ public void testNormalOperationWithRequestMatcherAndAuthenticationManager() thro
244249
assertThat(response.getRedirectedUrl()).isEqualTo("/mycontext/logged_in.jsp");
245250
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();
246251
assertThat(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString()).isEqualTo("test");
252+
assertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))
253+
.isNotNull();
247254
// Should still have the same session
248255
assertThat(request.getSession()).isEqualTo(sessionPreAuth);
249256
}
@@ -323,6 +330,8 @@ public void testSuccessfulAuthenticationInvokesSuccessHandlerAndSetsContext() th
323330
verify(successHandler).onAuthenticationSuccess(any(HttpServletRequest.class), any(HttpServletResponse.class),
324331
any(Authentication.class));
325332
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();
333+
assertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))
334+
.isNotNull();
326335
}
327336

328337
@Test

Diff for: web/src/test/java/org/springframework/security/web/authentication/AuthenticationFilterTests.java

+7
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.springframework.security.core.context.SecurityContextHolder;
4343
import org.springframework.security.core.context.SecurityContextHolderStrategy;
4444
import org.springframework.security.core.context.SecurityContextImpl;
45+
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
4546
import org.springframework.security.web.context.SecurityContextRepository;
4647
import org.springframework.security.web.util.matcher.RequestMatcher;
4748

@@ -128,6 +129,8 @@ public void filterWhenDefaultsAndAuthenticationSuccessThenContinues() throws Exc
128129
verify(this.authenticationManager).authenticate(any(Authentication.class));
129130
verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));
130131
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();
132+
assertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))
133+
.isNotNull();
131134
}
132135

133136
@Test
@@ -165,6 +168,8 @@ public void filterWhenAuthenticationManagerResolverDefaultsAndAuthenticationSucc
165168
verify(this.authenticationManager).authenticate(any(Authentication.class));
166169
verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));
167170
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();
171+
assertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))
172+
.isNotNull();
168173
}
169174

170175
@Test
@@ -228,6 +233,8 @@ public void filterWhenConvertAndAuthenticationSuccessThenSuccess() throws Except
228233
verify(this.successHandler).onAuthenticationSuccess(any(), any(), any(), eq(authentication));
229234
verifyNoMoreInteractions(this.failureHandler);
230235
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull();
236+
assertThat(request.getAttribute(RequestAttributeSecurityContextRepository.DEFAULT_REQUEST_ATTR_NAME))
237+
.isNotNull();
231238
}
232239

233240
@Test

0 commit comments

Comments
 (0)