diff --git a/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java b/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java index 1150200f05..1508cb46b4 100644 --- a/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java +++ b/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java @@ -236,6 +236,7 @@ final class AuthenticationConfigBuilder { AuthenticationConfigBuilder(Element element, boolean forceAutoConfig, ParserContext pc, SessionCreationPolicy sessionPolicy, BeanReference requestCache, BeanReference authenticationManager, + BeanReference authenticationFilterSecurityContextHolderStrategyRef, BeanReference authenticationFilterSecurityContextRepositoryRef, BeanReference sessionStrategy, BeanReference portMapper, BeanReference portResolver, BeanMetadataElement csrfLogoutHandler) { this.httpElt = element; @@ -247,11 +248,12 @@ final class AuthenticationConfigBuilder { this.portMapper = portMapper; this.portResolver = portResolver; this.csrfLogoutHandler = csrfLogoutHandler; - createAnonymousFilter(); + createAnonymousFilter(authenticationFilterSecurityContextHolderStrategyRef); createRememberMeFilter(authenticationManager); - createBasicFilter(authenticationManager); + createBasicFilter(authenticationManager, authenticationFilterSecurityContextHolderStrategyRef); createBearerTokenAuthenticationFilter(authenticationManager); - createFormLoginFilter(sessionStrategy, authenticationManager, authenticationFilterSecurityContextRepositoryRef); + createFormLoginFilter(sessionStrategy, authenticationManager, + authenticationFilterSecurityContextHolderStrategyRef, authenticationFilterSecurityContextRepositoryRef); createOAuth2ClientFilters(sessionStrategy, requestCache, authenticationManager, authenticationFilterSecurityContextRepositoryRef); createOpenIDLoginFilter(sessionStrategy, authenticationManager, @@ -259,11 +261,11 @@ final class AuthenticationConfigBuilder { createSaml2LoginFilter(authenticationManager, authenticationFilterSecurityContextRepositoryRef); createX509Filter(authenticationManager); createJeeFilter(authenticationManager); - createLogoutFilter(); + createLogoutFilter(authenticationFilterSecurityContextHolderStrategyRef); createSaml2LogoutFilter(); createLoginPageFilterIfNeeded(); createUserDetailsServiceFactory(); - createExceptionTranslationFilter(); + createExceptionTranslationFilter(authenticationFilterSecurityContextHolderStrategyRef); } void createRememberMeFilter(BeanReference authenticationManager) { @@ -293,6 +295,7 @@ final class AuthenticationConfigBuilder { } void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager, + BeanReference authenticationFilterSecurityContextHolderStrategyRef, BeanReference authenticationFilterSecurityContextRepositoryRef) { Element formLoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.FORM_LOGIN); RootBeanDefinition formFilter = null; @@ -313,6 +316,8 @@ final class AuthenticationConfigBuilder { formFilter.getPropertyValues().addPropertyValue("securityContextRepository", authenticationFilterSecurityContextRepositoryRef); } + formFilter.getPropertyValues().addPropertyValue("securityContextHolderStrategy", + authenticationFilterSecurityContextHolderStrategyRef); // Id is required by login page filter this.formFilterId = this.pc.getReaderContext().generateBeanName(formFilter); this.pc.registerBeanComponent(new BeanComponentDefinition(formFilter, this.formFilterId)); @@ -564,7 +569,8 @@ final class AuthenticationConfigBuilder { } } - void createBasicFilter(BeanReference authManager) { + void createBasicFilter(BeanReference authManager, + BeanReference authenticationFilterSecurityContextHolderStrategyRef) { Element basicAuthElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.BASIC_AUTH); if (basicAuthElt == null && !this.autoConfig) { // No basic auth, do nothing @@ -592,6 +598,8 @@ final class AuthenticationConfigBuilder { } filterBuilder.addConstructorArgValue(authManager); filterBuilder.addConstructorArgValue(this.basicEntryPoint); + filterBuilder.addPropertyValue("securityContextHolderStrategy", + authenticationFilterSecurityContextHolderStrategyRef); this.basicFilter = filterBuilder.getBeanDefinition(); } @@ -739,7 +747,7 @@ final class AuthenticationConfigBuilder { } } - void createLogoutFilter() { + void createLogoutFilter(BeanReference authenticationFilterSecurityContextHolderStrategyRef) { Element logoutElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.LOGOUT); if (logoutElt != null || this.autoConfig) { String formLoginPage = this.formLoginPage; @@ -747,7 +755,8 @@ final class AuthenticationConfigBuilder { formLoginPage = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL; } LogoutBeanDefinitionParser logoutParser = new LogoutBeanDefinitionParser(formLoginPage, - this.rememberMeServicesId, this.csrfLogoutHandler); + this.rememberMeServicesId, this.csrfLogoutHandler, + authenticationFilterSecurityContextHolderStrategyRef); this.logoutFilter = logoutParser.parse(logoutElt, this.pc); this.logoutHandlers = logoutParser.getLogoutHandlers(); this.logoutSuccessHandler = logoutParser.getLogoutSuccessHandler(); @@ -803,7 +812,7 @@ final class AuthenticationConfigBuilder { return this.csrfIgnoreRequestMatchers; } - void createAnonymousFilter() { + void createAnonymousFilter(BeanReference authenticationFilterSecurityContextHolderStrategyRef) { Element anonymousElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.ANONYMOUS); if (anonymousElt != null && "false".equals(anonymousElt.getAttribute("enabled"))) { return; @@ -833,6 +842,8 @@ final class AuthenticationConfigBuilder { this.anonymousFilter.getConstructorArgumentValues().addIndexedArgumentValue(1, username); this.anonymousFilter.getConstructorArgumentValues().addIndexedArgumentValue(2, AuthorityUtils.commaSeparatedStringToAuthorityList(grantedAuthority)); + this.anonymousFilter.getPropertyValues().addPropertyValue("securityContextHolderStrategy", + authenticationFilterSecurityContextHolderStrategyRef); this.anonymousFilter.setSource(source); RootBeanDefinition anonymousProviderBean = new RootBeanDefinition(AnonymousAuthenticationProvider.class); anonymousProviderBean.getConstructorArgumentValues().addIndexedArgumentValue(0, key); @@ -847,7 +858,7 @@ final class AuthenticationConfigBuilder { return Long.toString(random.nextLong()); } - void createExceptionTranslationFilter() { + void createExceptionTranslationFilter(BeanReference authenticationFilterSecurityContextHolderStrategyRef) { BeanDefinitionBuilder etfBuilder = BeanDefinitionBuilder.rootBeanDefinition(ExceptionTranslationFilter.class); this.accessDeniedHandler = createAccessDeniedHandler(this.httpElt, this.pc); etfBuilder.addPropertyValue("accessDeniedHandler", this.accessDeniedHandler); @@ -855,6 +866,8 @@ final class AuthenticationConfigBuilder { this.mainEntryPoint = selectEntryPoint(); etfBuilder.addConstructorArgValue(this.mainEntryPoint); etfBuilder.addConstructorArgValue(this.requestCache); + etfBuilder.addPropertyValue("securityContextHolderStrategy", + authenticationFilterSecurityContextHolderStrategyRef); this.etf = etfBuilder.getBeanDefinition(); } diff --git a/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java b/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java index e7539558b5..ca4ee8d828 100644 --- a/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java +++ b/config/src/main/java/org/springframework/security/config/http/HttpConfigurationBuilder.java @@ -24,6 +24,7 @@ import javax.servlet.ServletRequest; import org.w3c.dom.Element; import org.springframework.beans.BeanMetadataElement; +import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanReference; import org.springframework.beans.factory.config.RuntimeBeanReference; @@ -40,6 +41,8 @@ import org.springframework.security.access.vote.AuthenticatedVoter; import org.springframework.security.access.vote.RoleVoter; import org.springframework.security.config.Elements; import org.springframework.security.config.http.GrantedAuthorityDefaultsParserUtils.AbstractGrantedAuthorityDefaultsBeanFactory; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator; import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator; @@ -106,6 +109,8 @@ class HttpConfigurationBuilder { private static final String ATT_SESSION_AUTH_ERROR_URL = "session-authentication-error-url"; + private static final String ATT_SECURITY_CONTEXT_HOLDER_STRATEGY = "security-context-holder-strategy-ref"; + private static final String ATT_SECURITY_CONTEXT_REPOSITORY = "security-context-repository-ref"; private static final String ATT_SECURITY_CONTEXT_EXPLICIT_SAVE = "security-context-explicit-save"; @@ -156,6 +161,8 @@ class HttpConfigurationBuilder { private BeanDefinition forceEagerSessionCreationFilter; + private BeanReference holderStrategyRef; + private BeanReference contextRepoRef; private BeanReference sessionRegistryRef; @@ -215,6 +222,7 @@ class HttpConfigurationBuilder { String createSession = element.getAttribute(ATT_CREATE_SESSION); this.sessionPolicy = !StringUtils.hasText(createSession) ? SessionCreationPolicy.IF_REQUIRED : createPolicy(createSession); + createSecurityContextHolderStrategy(); createForceEagerSessionCreationFilter(); createDisableEncodeUrlFilter(); createCsrfFilter(); @@ -294,6 +302,10 @@ class HttpConfigurationBuilder { return lowerCase ? path.toLowerCase() : path; } + BeanReference getSecurityContextHolderStrategyForAuthenticationFilters() { + return this.holderStrategyRef; + } + BeanReference getSecurityContextRepositoryForAuthenticationFilters() { return (isExplicitSave()) ? this.contextRepoRef : null; } @@ -331,11 +343,23 @@ class HttpConfigurationBuilder { default: scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE); } + scpf.addPropertyValue("securityContextHolderStrategy", this.holderStrategyRef); scpf.addConstructorArgValue(this.contextRepoRef); this.securityContextPersistenceFilter = scpf.getBeanDefinition(); } + private void createSecurityContextHolderStrategy() { + String holderStrategyRef = this.httpElt.getAttribute(ATT_SECURITY_CONTEXT_HOLDER_STRATEGY); + if (!StringUtils.hasText(holderStrategyRef)) { + BeanDefinition holderStrategyBean = BeanDefinitionBuilder + .rootBeanDefinition(SecurityContextHolderStrategyFactory.class).getBeanDefinition(); + holderStrategyRef = this.pc.getReaderContext().generateBeanName(holderStrategyBean); + this.pc.registerBeanComponent(new BeanComponentDefinition(holderStrategyBean, holderStrategyRef)); + } + this.holderStrategyRef = new RuntimeBeanReference(holderStrategyRef); + } + private void createSecurityContextRepository() { String repoRef = this.httpElt.getAttribute(ATT_SECURITY_CONTEXT_REPOSITORY); if (!StringUtils.hasText(repoRef)) { @@ -359,6 +383,7 @@ class HttpConfigurationBuilder { contextRepo.addPropertyValue("disableUrlRewriting", Boolean.TRUE); } } + contextRepo.addPropertyValue("securityContextHolderStrategy", this.holderStrategyRef); BeanDefinition repoBean = contextRepo.getBeanDefinition(); repoRef = this.pc.getReaderContext().generateBeanName(repoBean); this.pc.registerBeanComponent(new BeanComponentDefinition(repoBean, repoRef)); @@ -374,6 +399,7 @@ class HttpConfigurationBuilder { private void createSecurityContextHolderFilter() { BeanDefinitionBuilder filter = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextHolderFilter.class); + filter.addPropertyValue("securityContextHolderStrategy", this.holderStrategyRef); filter.addConstructorArgValue(this.contextRepoRef); this.securityContextPersistenceFilter = filter.getBeanDefinition(); } @@ -485,6 +511,7 @@ class HttpConfigurationBuilder { if (StringUtils.hasText(errorUrl)) { failureHandler.getPropertyValues().addPropertyValue("defaultFailureUrl", errorUrl); } + sessionMgmtFilter.addPropertyValue("securityContextHolderStrategy", this.holderStrategyRef); sessionMgmtFilter.addPropertyValue("authenticationFailureHandler", failureHandler); sessionMgmtFilter.addConstructorArgValue(this.contextRepoRef); if (!StringUtils.hasText(sessionAuthStratRef) && sessionFixationStrategy != null && !useChangeSessionId) { @@ -744,6 +771,7 @@ class HttpConfigurationBuilder { builder.addPropertyValue("observeOncePerRequest", Boolean.FALSE); } builder.addPropertyValue("securityMetadataSource", securityMds); + builder.addPropertyValue("securityContextHolderStrategy", this.holderStrategyRef); BeanDefinition fsiBean = builder.getBeanDefinition(); String fsiId = this.pc.getReaderContext().generateBeanName(fsiBean); this.pc.registerBeanComponent(new BeanComponentDefinition(fsiBean, fsiId)); @@ -883,4 +911,18 @@ class HttpConfigurationBuilder { } + static class SecurityContextHolderStrategyFactory implements FactoryBean { + + @Override + public SecurityContextHolderStrategy getObject() throws Exception { + return SecurityContextHolder.getContextHolderStrategy(); + } + + @Override + public Class getObjectType() { + return SecurityContextHolderStrategy.class; + } + + } + } diff --git a/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java index 7d0be016ce..1bfe7811f2 100644 --- a/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/http/HttpSecurityBeanDefinitionParser.java @@ -147,6 +147,7 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser { httpBldr.getSecurityContextRepositoryForAuthenticationFilters(); AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc, httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager, + httpBldr.getSecurityContextHolderStrategyForAuthenticationFilters(), httpBldr.getSecurityContextRepositoryForAuthenticationFilters(), httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler()); httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers()); diff --git a/config/src/main/java/org/springframework/security/config/http/LogoutBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/LogoutBeanDefinitionParser.java index 51d9462d5e..f7f4a4ec0d 100644 --- a/config/src/main/java/org/springframework/security/config/http/LogoutBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/http/LogoutBeanDefinitionParser.java @@ -20,6 +20,7 @@ import org.w3c.dom.Element; import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanReference; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.ManagedList; @@ -61,13 +62,17 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser { private BeanMetadataElement logoutSuccessHandler; - LogoutBeanDefinitionParser(String loginPageUrl, String rememberMeServices, BeanMetadataElement csrfLogoutHandler) { + private BeanReference authenticationFilterSecurityContextHolderStrategyRef; + + LogoutBeanDefinitionParser(String loginPageUrl, String rememberMeServices, BeanMetadataElement csrfLogoutHandler, + BeanReference authenticationFilterSecurityContextHolderStrategyRef) { this.defaultLogoutUrl = loginPageUrl + "?logout"; this.rememberMeServices = rememberMeServices; this.csrfEnabled = csrfLogoutHandler != null; if (this.csrfEnabled) { this.logoutHandlers.add(csrfLogoutHandler); } + this.authenticationFilterSecurityContextHolderStrategyRef = authenticationFilterSecurityContextHolderStrategyRef; } @Override @@ -123,6 +128,8 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser { } this.logoutHandlers.add(new RootBeanDefinition(LogoutSuccessEventPublishingLogoutHandler.class)); builder.addConstructorArgValue(this.logoutHandlers); + builder.addPropertyValue("securityContextHolderStrategy", + this.authenticationFilterSecurityContextHolderStrategyRef); return builder.getBeanDefinition(); } diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-5.8.rnc b/config/src/main/resources/org/springframework/security/config/spring-security-5.8.rnc index 1db6c30464..c2c7812b5e 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-5.8.rnc +++ b/config/src/main/resources/org/springframework/security/config/spring-security-5.8.rnc @@ -333,6 +333,9 @@ http.attlist &= attribute auto-config {xsd:boolean}? http.attlist &= use-expressions? +http.attlist &= + ## A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the SecurityContextHolder is stored during a request + attribute security-context-holder-strategy-ref {xsd:token}? http.attlist &= ## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to "ifRequired". If "stateless" is used, this implies that the application guarantees that it will not create a session. This differs from the use of "never" which means that Spring Security will not create a session, but will make use of one if the application does. attribute create-session {"ifRequired" | "always" | "never" | "stateless"}? diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-5.8.xsd b/config/src/main/resources/org/springframework/security/config/spring-security-5.8.xsd index 2d3538a09d..010f8c32bb 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-5.8.xsd +++ b/config/src/main/resources/org/springframework/security/config/spring-security-5.8.xsd @@ -1224,6 +1224,13 @@ + + + A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the + SecurityContextHolder is stored during a request + + + Controls the eagerness with which an HTTP session is created by Spring Security classes. diff --git a/config/src/test/java/org/springframework/security/config/http/FormLoginConfigTests.java b/config/src/test/java/org/springframework/security/config/http/FormLoginConfigTests.java index c03c855c47..3ba5af80d8 100644 --- a/config/src/test/java/org/springframework/security/config/http/FormLoginConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/FormLoginConfigTests.java @@ -34,6 +34,7 @@ import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; @@ -45,6 +46,8 @@ import org.springframework.web.bind.annotation.RestController; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -147,6 +150,14 @@ public class FormLoginConfigTests { .andExpect(redirectedUrl("/")); } + @Test + public void authenticateWhenCustomSecurityContextHolderStrategyThenUses() throws Exception { + this.spring.configLocations(this.xml("WithCustomSecurityContextHolderStrategy")).autowire(); + SecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class); + this.mvc.perform(post("/login").with(csrf())).andExpect(redirectedUrl("/login?error")); + verify(strategy, atLeastOnce()).getContext(); + } + /** * SEC-2919 - DefaultLoginGeneratingFilter incorrectly used if login-url="/login" */ diff --git a/config/src/test/java/org/springframework/security/config/http/NamespaceHttpBasicTests.java b/config/src/test/java/org/springframework/security/config/http/NamespaceHttpBasicTests.java index 1616a8b6c7..5d2031c991 100644 --- a/config/src/test/java/org/springframework/security/config/http/NamespaceHttpBasicTests.java +++ b/config/src/test/java/org/springframework/security/config/http/NamespaceHttpBasicTests.java @@ -32,8 +32,11 @@ import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.security.config.util.InMemoryXmlApplicationContext; +import org.springframework.security.core.context.SecurityContextHolderStrategy; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.verify; /** * @author Rob Winch @@ -94,6 +97,30 @@ public class NamespaceHttpBasicTests { assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK); } + @Test + public void httpBasicCustomSecurityContextHolderStrategy() throws Exception { + // @formatter:off + loadContext("\n" + + "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n" + + "\n" + + " \n" + + " \n" + + " \n" + + ""); + // @formatter:on + this.request.addHeader("Authorization", + "Basic " + Base64.getEncoder().encodeToString("user:test".getBytes("UTF-8"))); + this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain); + assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK); + verify(this.context.getBean(SecurityContextHolderStrategy.class), atLeastOnce()).getContext(); + } + // gh-4220 @Test public void httpBasicUnauthorizedOnDefault() throws Exception { diff --git a/config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-WithCustomSecurityContextHolderStrategy.xml b/config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-WithCustomSecurityContextHolderStrategy.xml new file mode 100644 index 0000000000..7e8f943870 --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/http/FormLoginConfigTests-WithCustomSecurityContextHolderStrategy.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + diff --git a/docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc b/docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc index 1f26ad0f45..d4790cc96e 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/namespace/http.adoc @@ -131,6 +131,9 @@ This is a more powerful alternative to <>. A request pattern can be mapped to an empty filter chain, by setting this attribute to `none`. No security will be applied and none of Spring Security's features will be available. +[[nsa-http-security-context-holder-strategy-ref]] +* **security-context-repository-ref** +Allows injection of a custom `SecurityContextHolderStrategy` into `SecurityContextPersistenceFilter`, `SecurityContextHolderFilter`, `BasicAuthenticationFilter`, `UsernamePasswordAuthenticationFilter`, `ExceptionTranslationFilter`, `LogoutFilter`, and others. [[nsa-http-security-context-explicit-save]] * **security-context-explicit-save**