Skip to content

Commit 87ed31a

Browse files
committed
Add SecurityContextHolderFilter
Closes gh-9635
1 parent dbcb500 commit 87ed31a

File tree

22 files changed

+582
-45
lines changed

22 files changed

+582
-45
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterOrderRegistration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
3838
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
3939
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;
40+
import org.springframework.security.web.context.SecurityContextHolderFilter;
4041
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
4142
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
4243
import org.springframework.security.web.csrf.CsrfFilter;
@@ -70,6 +71,7 @@ final class FilterOrderRegistration {
7071
put(ChannelProcessingFilter.class, order.next());
7172
order.next(); // gh-8105
7273
put(WebAsyncManagerIntegrationFilter.class, order.next());
74+
put(SecurityContextHolderFilter.class, order.next());
7375
put(SecurityContextPersistenceFilter.class, order.next());
7476
put(HeaderWriterFilter.class, order.next());
7577
put(CorsFilter.class, order.next());

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
3939
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
4040
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
41+
import org.springframework.security.web.context.SecurityContextRepository;
4142
import org.springframework.security.web.savedrequest.RequestCache;
4243
import org.springframework.security.web.util.matcher.AndRequestMatcher;
4344
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
@@ -146,6 +147,11 @@ public T loginProcessingUrl(String loginProcessingUrl) {
146147
return getSelf();
147148
}
148149

150+
public T securityContextRepository(SecurityContextRepository securityContextRepository) {
151+
this.authFilter.setSecurityContextRepository(securityContextRepository);
152+
return getSelf();
153+
}
154+
149155
/**
150156
* Create the {@link RequestMatcher} given a loginProcessingUrl
151157
* @param loginProcessingUrl creates the {@link RequestMatcher} based upon the
@@ -287,6 +293,12 @@ public void configure(B http) throws Exception {
287293
if (rememberMeServices != null) {
288294
this.authFilter.setRememberMeServices(rememberMeServices);
289295
}
296+
SecurityContextConfigurer securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);
297+
if (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {
298+
SecurityContextRepository securityContextRepository = securityContextConfigurer
299+
.getSecurityContextRepository();
300+
this.authFilter.setSecurityContextRepository(securityContextRepository);
301+
}
290302
F filter = postProcess(this.authFilter);
291303
http.addFilter(filter);
292304
}

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

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.springframework.security.core.context.SecurityContext;
2323
import org.springframework.security.core.context.SecurityContextHolder;
2424
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
25+
import org.springframework.security.web.context.SecurityContextHolderFilter;
2526
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
2627
import org.springframework.security.web.context.SecurityContextRepository;
2728

@@ -62,6 +63,8 @@
6263
public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>>
6364
extends AbstractHttpConfigurer<SecurityContextConfigurer<H>, H> {
6465

66+
private boolean requireExplicitSave;
67+
6568
/**
6669
* Creates a new instance
6770
* @see HttpSecurity#securityContext()
@@ -79,23 +82,45 @@ public SecurityContextConfigurer<H> securityContextRepository(SecurityContextRep
7982
return this;
8083
}
8184

85+
public SecurityContextConfigurer<H> requireExplicitSave(boolean requireExplicitSave) {
86+
this.requireExplicitSave = requireExplicitSave;
87+
return this;
88+
}
89+
90+
boolean isRequireExplicitSave() {
91+
return this.requireExplicitSave;
92+
}
93+
94+
SecurityContextRepository getSecurityContextRepository() {
95+
SecurityContextRepository securityContextRepository = getBuilder()
96+
.getSharedObject(SecurityContextRepository.class);
97+
if (securityContextRepository == null) {
98+
securityContextRepository = new HttpSessionSecurityContextRepository();
99+
}
100+
return securityContextRepository;
101+
}
102+
82103
@Override
83104
@SuppressWarnings("unchecked")
84105
public void configure(H http) {
85-
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
86-
if (securityContextRepository == null) {
87-
securityContextRepository = new HttpSessionSecurityContextRepository();
106+
SecurityContextRepository securityContextRepository = getSecurityContextRepository();
107+
if (this.requireExplicitSave) {
108+
SecurityContextHolderFilter securityContextHolderFilter = postProcess(
109+
new SecurityContextHolderFilter(securityContextRepository));
110+
http.addFilter(securityContextHolderFilter);
88111
}
89-
SecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter(
90-
securityContextRepository);
91-
SessionManagementConfigurer<?> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
92-
SessionCreationPolicy sessionCreationPolicy = (sessionManagement != null)
93-
? sessionManagement.getSessionCreationPolicy() : null;
94-
if (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) {
95-
securityContextFilter.setForceEagerSessionCreation(true);
112+
else {
113+
SecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter(
114+
securityContextRepository);
115+
SessionManagementConfigurer<?> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
116+
SessionCreationPolicy sessionCreationPolicy = (sessionManagement != null)
117+
? sessionManagement.getSessionCreationPolicy() : null;
118+
if (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) {
119+
securityContextFilter.setForceEagerSessionCreation(true);
120+
}
121+
securityContextFilter = postProcess(securityContextFilter);
122+
http.addFilter(securityContextFilter);
96123
}
97-
securityContextFilter = postProcess(securityContextFilter);
98-
http.addFilter(securityContextFilter);
99124
}
100125

101126
}

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

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,8 @@ final class AuthenticationConfigBuilder {
236236

237237
AuthenticationConfigBuilder(Element element, boolean forceAutoConfig, ParserContext pc,
238238
SessionCreationPolicy sessionPolicy, BeanReference requestCache, BeanReference authenticationManager,
239-
BeanReference sessionStrategy, BeanReference portMapper, BeanReference portResolver,
240-
BeanMetadataElement csrfLogoutHandler) {
239+
BeanReference authenticationFilterSecurityContextRepositoryRef, BeanReference sessionStrategy,
240+
BeanReference portMapper, BeanReference portResolver, BeanMetadataElement csrfLogoutHandler) {
241241
this.httpElt = element;
242242
this.pc = pc;
243243
this.requestCache = requestCache;
@@ -251,10 +251,12 @@ final class AuthenticationConfigBuilder {
251251
createRememberMeFilter(authenticationManager);
252252
createBasicFilter(authenticationManager);
253253
createBearerTokenAuthenticationFilter(authenticationManager);
254-
createFormLoginFilter(sessionStrategy, authenticationManager);
255-
createOAuth2ClientFilters(sessionStrategy, requestCache, authenticationManager);
256-
createOpenIDLoginFilter(sessionStrategy, authenticationManager);
257-
createSaml2LoginFilter(authenticationManager);
254+
createFormLoginFilter(sessionStrategy, authenticationManager, authenticationFilterSecurityContextRepositoryRef);
255+
createOAuth2ClientFilters(sessionStrategy, requestCache, authenticationManager,
256+
authenticationFilterSecurityContextRepositoryRef);
257+
createOpenIDLoginFilter(sessionStrategy, authenticationManager,
258+
authenticationFilterSecurityContextRepositoryRef);
259+
createSaml2LoginFilter(authenticationManager, authenticationFilterSecurityContextRepositoryRef);
258260
createX509Filter(authenticationManager);
259261
createJeeFilter(authenticationManager);
260262
createLogoutFilter();
@@ -290,7 +292,8 @@ private void createRememberMeProvider(String key) {
290292
this.rememberMeProviderRef = new RuntimeBeanReference(id);
291293
}
292294

293-
void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
295+
void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager,
296+
BeanReference authenticationFilterSecurityContextRepositoryRef) {
294297
Element formLoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.FORM_LOGIN);
295298
RootBeanDefinition formFilter = null;
296299
if (formLoginElt != null || this.autoConfig) {
@@ -306,6 +309,10 @@ void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authMana
306309
if (formFilter != null) {
307310
formFilter.getPropertyValues().addPropertyValue("allowSessionCreation", this.allowSessionCreation);
308311
formFilter.getPropertyValues().addPropertyValue("authenticationManager", authManager);
312+
if (authenticationFilterSecurityContextRepositoryRef != null) {
313+
formFilter.getPropertyValues().addPropertyValue("securityContextRepository",
314+
authenticationFilterSecurityContextRepositoryRef);
315+
}
309316
// Id is required by login page filter
310317
this.formFilterId = this.pc.getReaderContext().generateBeanName(formFilter);
311318
this.pc.registerBeanComponent(new BeanComponentDefinition(formFilter, this.formFilterId));
@@ -314,13 +321,15 @@ void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authMana
314321
}
315322

316323
void createOAuth2ClientFilters(BeanReference sessionStrategy, BeanReference requestCache,
317-
BeanReference authenticationManager) {
318-
createOAuth2LoginFilter(sessionStrategy, authenticationManager);
319-
createOAuth2ClientFilter(requestCache, authenticationManager);
324+
BeanReference authenticationManager, BeanReference authenticationFilterSecurityContextRepositoryRef) {
325+
createOAuth2LoginFilter(sessionStrategy, authenticationManager,
326+
authenticationFilterSecurityContextRepositoryRef);
327+
createOAuth2ClientFilter(requestCache, authenticationManager, authenticationFilterSecurityContextRepositoryRef);
320328
registerOAuth2ClientPostProcessors();
321329
}
322330

323-
void createOAuth2LoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
331+
void createOAuth2LoginFilter(BeanReference sessionStrategy, BeanReference authManager,
332+
BeanReference authenticationFilterSecurityContextRepositoryRef) {
324333
Element oauth2LoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_LOGIN);
325334
if (oauth2LoginElt == null) {
326335
return;
@@ -332,6 +341,10 @@ void createOAuth2LoginFilter(BeanReference sessionStrategy, BeanReference authMa
332341
BeanDefinition defaultAuthorizedClientRepository = parser.getDefaultAuthorizedClientRepository();
333342
registerDefaultAuthorizedClientRepositoryIfNecessary(defaultAuthorizedClientRepository);
334343
oauth2LoginFilterBean.getPropertyValues().addPropertyValue("authenticationManager", authManager);
344+
if (authenticationFilterSecurityContextRepositoryRef != null) {
345+
oauth2LoginFilterBean.getPropertyValues().addPropertyValue("securityContextRepository",
346+
authenticationFilterSecurityContextRepositoryRef);
347+
}
335348

336349
// retrieve the other bean result
337350
BeanDefinition oauth2LoginAuthProvider = parser.getOAuth2LoginAuthenticationProvider();
@@ -361,14 +374,15 @@ void createOAuth2LoginFilter(BeanReference sessionStrategy, BeanReference authMa
361374
this.oauth2LoginOidcAuthenticationProviderRef = new RuntimeBeanReference(oauth2LoginOidcAuthProviderId);
362375
}
363376

364-
void createOAuth2ClientFilter(BeanReference requestCache, BeanReference authenticationManager) {
377+
void createOAuth2ClientFilter(BeanReference requestCache, BeanReference authenticationManager,
378+
BeanReference authenticationFilterSecurityContextRepositoryRef) {
365379
Element oauth2ClientElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_CLIENT);
366380
if (oauth2ClientElt == null) {
367381
return;
368382
}
369383
this.oauth2ClientEnabled = true;
370384
OAuth2ClientBeanDefinitionParser parser = new OAuth2ClientBeanDefinitionParser(requestCache,
371-
authenticationManager);
385+
authenticationManager, authenticationFilterSecurityContextRepositoryRef);
372386
parser.parse(oauth2ClientElt, this.pc);
373387
BeanDefinition defaultAuthorizedClientRepository = parser.getDefaultAuthorizedClientRepository();
374388
registerDefaultAuthorizedClientRepositoryIfNecessary(defaultAuthorizedClientRepository);
@@ -413,7 +427,8 @@ private void registerOAuth2ClientPostProcessors() {
413427
}
414428
}
415429

416-
void createOpenIDLoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
430+
void createOpenIDLoginFilter(BeanReference sessionStrategy, BeanReference authManager,
431+
BeanReference authenticationFilterSecurityContextRepositoryRef) {
417432
Element openIDLoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OPENID_LOGIN);
418433
RootBeanDefinition openIDFilter = null;
419434
if (openIDLoginElt != null) {
@@ -422,6 +437,10 @@ void createOpenIDLoginFilter(BeanReference sessionStrategy, BeanReference authMa
422437
if (openIDFilter != null) {
423438
openIDFilter.getPropertyValues().addPropertyValue("allowSessionCreation", this.allowSessionCreation);
424439
openIDFilter.getPropertyValues().addPropertyValue("authenticationManager", authManager);
440+
if (authenticationFilterSecurityContextRepositoryRef != null) {
441+
openIDFilter.getPropertyValues().addPropertyValue("securityContextRepository",
442+
authenticationFilterSecurityContextRepositoryRef);
443+
}
425444
// Required by login page filter
426445
this.openIDFilterId = this.pc.getReaderContext().generateBeanName(openIDFilter);
427446
this.pc.registerBeanComponent(new BeanComponentDefinition(openIDFilter, this.openIDFilterId));
@@ -430,14 +449,16 @@ void createOpenIDLoginFilter(BeanReference sessionStrategy, BeanReference authMa
430449
}
431450
}
432451

433-
private void createSaml2LoginFilter(BeanReference authenticationManager) {
452+
private void createSaml2LoginFilter(BeanReference authenticationManager,
453+
BeanReference authenticationFilterSecurityContextRepositoryRef) {
434454
Element saml2LoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.SAML2_LOGIN);
435455
if (saml2LoginElt == null) {
436456
return;
437457
}
438458
Saml2LoginBeanDefinitionParser parser = new Saml2LoginBeanDefinitionParser(this.csrfIgnoreRequestMatchers,
439459
this.portMapper, this.portResolver, this.requestCache, this.allowSessionCreation, authenticationManager,
440-
this.authenticationProviders, this.defaultEntryPointMappings);
460+
authenticationFilterSecurityContextRepositoryRef, this.authenticationProviders,
461+
this.defaultEntryPointMappings);
441462
BeanDefinition saml2WebSsoAuthenticationFilter = parser.parse(saml2LoginElt, this.pc);
442463
this.saml2AuthorizationRequestFilter = parser.getSaml2WebSsoAuthenticationRequestFilter();
443464

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

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
6060
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
6161
import org.springframework.security.web.context.NullSecurityContextRepository;
62+
import org.springframework.security.web.context.SecurityContextHolderFilter;
6263
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
6364
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
6465
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
@@ -104,6 +105,8 @@ class HttpConfigurationBuilder {
104105

105106
private static final String ATT_SECURITY_CONTEXT_REPOSITORY = "security-context-repository-ref";
106107

108+
private static final String ATT_SECURITY_CONTEXT_EXPLICIT_SAVE = "security-context-explicit-save";
109+
107110
private static final String ATT_INVALID_SESSION_STRATEGY_REF = "invalid-session-strategy-ref";
108111

109112
private static final String ATT_DISABLE_URL_REWRITING = "disable-url-rewriting";
@@ -202,8 +205,7 @@ class HttpConfigurationBuilder {
202205
this.sessionPolicy = !StringUtils.hasText(createSession) ? SessionCreationPolicy.IF_REQUIRED
203206
: createPolicy(createSession);
204207
createCsrfFilter();
205-
createSecurityContextRepository();
206-
createSecurityContextPersistenceFilter();
208+
createSecurityPersistence();
207209
createSessionManagementFilters();
208210
createWebAsyncManagerFilter();
209211
createRequestCacheFilter();
@@ -279,9 +281,27 @@ static String createPath(String path, boolean lowerCase) {
279281
return lowerCase ? path.toLowerCase() : path;
280282
}
281283

284+
BeanReference getSecurityContextRepositoryForAuthenticationFilters() {
285+
return (isExplicitSave()) ? this.contextRepoRef : null;
286+
}
287+
288+
private void createSecurityPersistence() {
289+
createSecurityContextRepository();
290+
if (isExplicitSave()) {
291+
createSecurityContextHolderFilter();
292+
}
293+
else {
294+
createSecurityContextPersistenceFilter();
295+
}
296+
}
297+
298+
private boolean isExplicitSave() {
299+
String explicitSaveAttr = this.httpElt.getAttribute(ATT_SECURITY_CONTEXT_EXPLICIT_SAVE);
300+
return Boolean.parseBoolean(explicitSaveAttr);
301+
}
302+
282303
private void createSecurityContextPersistenceFilter() {
283304
BeanDefinitionBuilder scpf = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextPersistenceFilter.class);
284-
String disableUrlRewriting = this.httpElt.getAttribute(ATT_DISABLE_URL_REWRITING);
285305
switch (this.sessionPolicy) {
286306
case ALWAYS:
287307
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
@@ -332,6 +352,12 @@ private void createSecurityContextRepository() {
332352
this.contextRepoRef = new RuntimeBeanReference(repoRef);
333353
}
334354

355+
private void createSecurityContextHolderFilter() {
356+
BeanDefinitionBuilder filter = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextHolderFilter.class);
357+
filter.addConstructorArgValue(this.contextRepoRef);
358+
this.securityContextPersistenceFilter = filter.getBeanDefinition();
359+
}
360+
335361
private void createSessionManagementFilters() {
336362
Element sessionMgmtElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.SESSION_MANAGEMENT);
337363
Element sessionCtrlElt = null;

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,11 @@ private BeanReference createFilterChain(Element element, ParserContext pc) {
144144
boolean forceAutoConfig = isDefaultHttpConfig(element);
145145
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper,
146146
portResolver, authenticationManager);
147+
httpBldr.getSecurityContextRepositoryForAuthenticationFilters();
147148
AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc,
148149
httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager,
149-
httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler());
150+
httpBldr.getSecurityContextRepositoryForAuthenticationFilters(), httpBldr.getSessionStrategy(),
151+
portMapper, portResolver, httpBldr.getCsrfLogoutHandler());
150152
httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
151153
httpBldr.setEntryPoint(authBldr.getEntryPointBean());
152154
httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());

0 commit comments

Comments
 (0)