diff --git a/config/src/main/java/org/springframework/security/config/method/MethodSecurityBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/method/MethodSecurityBeanDefinitionParser.java index cbb1763872..76c4fa82df 100644 --- a/config/src/main/java/org/springframework/security/config/method/MethodSecurityBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/method/MethodSecurityBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,9 +21,11 @@ import org.apache.commons.logging.LogFactory; import org.w3c.dom.Element; import org.springframework.aop.config.AopNamespaceUtils; +import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.BeansException; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.parsing.CompositeComponentDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.BeanDefinitionParser; @@ -41,6 +43,9 @@ import org.springframework.security.authorization.method.PreAuthorizeAuthorizati import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor; import org.springframework.security.config.Elements; import org.springframework.security.config.core.GrantedAuthorityDefaults; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.context.SecurityContextHolderStrategy; +import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; /** @@ -61,26 +66,33 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser private static final String ATT_REF = "ref"; + private static final String ATT_SECURITY_CONTEXT_HOLDER_STRATEGY_REF = "security-context-holder-strategy-ref"; + @Override public BeanDefinition parse(Element element, ParserContext pc) { CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), pc.extractSource(element)); pc.pushContainingComponent(compositeDef); + BeanMetadataElement securityContextHolderStrategy = getSecurityContextHolderStrategy(element); boolean prePostAnnotationsEnabled = !element.hasAttribute(ATT_USE_PREPOST) || "true".equals(element.getAttribute(ATT_USE_PREPOST)); if (prePostAnnotationsEnabled) { BeanDefinitionBuilder preFilterInterceptor = BeanDefinitionBuilder .rootBeanDefinition(PreFilterAuthorizationMethodInterceptor.class) - .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE) + .addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy); BeanDefinitionBuilder preAuthorizeInterceptor = BeanDefinitionBuilder .rootBeanDefinition(PreAuthorizeAuthorizationMethodInterceptor.class) - .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE) + .addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy); BeanDefinitionBuilder postAuthorizeInterceptor = BeanDefinitionBuilder .rootBeanDefinition(PostAuthorizeAuthorizationMethodInterceptor.class) - .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE) + .addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy); BeanDefinitionBuilder postFilterInterceptor = BeanDefinitionBuilder .rootBeanDefinition(PostFilterAuthorizationMethodInterceptor.class) - .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE) + .addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy); Element expressionHandlerElt = DomUtils.getChildElementByTagName(element, Elements.EXPRESSION_HANDLER); if (expressionHandlerElt != null) { String expressionHandlerRef = expressionHandlerElt.getAttribute(ATT_REF); @@ -110,7 +122,9 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser if (securedEnabled) { BeanDefinitionBuilder securedInterceptor = BeanDefinitionBuilder .rootBeanDefinition(AuthorizationManagerBeforeMethodInterceptor.class) - .setRole(BeanDefinition.ROLE_INFRASTRUCTURE).setFactoryMethod("secured"); + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE) + .addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy) + .setFactoryMethod("secured"); pc.getRegistry().registerBeanDefinition("securedAuthorizationMethodInterceptor", securedInterceptor.getBeanDefinition()); } @@ -118,7 +132,8 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser if (jsr250Enabled) { BeanDefinitionBuilder jsr250Interceptor = BeanDefinitionBuilder .rootBeanDefinition(Jsr250AuthorizationMethodInterceptor.class) - .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE) + .addPropertyValue("securityContextHolderStrategy", securityContextHolderStrategy); pc.getRegistry().registerBeanDefinition("jsr250AuthorizationMethodInterceptor", jsr250Interceptor.getBeanDefinition()); } @@ -127,6 +142,14 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser return null; } + private BeanMetadataElement getSecurityContextHolderStrategy(Element methodSecurityElmt) { + String holderStrategyRef = methodSecurityElmt.getAttribute(ATT_SECURITY_CONTEXT_HOLDER_STRATEGY_REF); + if (StringUtils.hasText(holderStrategyRef)) { + return new RuntimeBeanReference(holderStrategyRef); + } + return BeanDefinitionBuilder.rootBeanDefinition(SecurityContextHolderStrategyFactory.class).getBeanDefinition(); + } + public static final class MethodSecurityExpressionHandlerBean implements FactoryBean, ApplicationContextAware { @@ -158,11 +181,17 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser public static final class Jsr250AuthorizationMethodInterceptor implements FactoryBean, ApplicationContextAware { + private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder + .getContextHolderStrategy(); + private final Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); @Override public AuthorizationManagerBeforeMethodInterceptor getObject() { - return AuthorizationManagerBeforeMethodInterceptor.jsr250(this.manager); + AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor + .jsr250(this.manager); + interceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy); + return interceptor; } @Override @@ -181,16 +210,26 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser } } + public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) { + this.securityContextHolderStrategy = securityContextHolderStrategy; + } + } public static final class PreAuthorizeAuthorizationMethodInterceptor implements FactoryBean { + private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder + .getContextHolderStrategy(); + private final PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); @Override public AuthorizationManagerBeforeMethodInterceptor getObject() { - return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(this.manager); + AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor + .preAuthorize(this.manager); + interceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy); + return interceptor; } @Override @@ -198,6 +237,10 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser return AuthorizationManagerBeforeMethodInterceptor.class; } + public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) { + this.securityContextHolderStrategy = securityContextHolderStrategy; + } + public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) { this.manager.setExpressionHandler(expressionHandler); } @@ -207,11 +250,17 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser public static final class PostAuthorizeAuthorizationMethodInterceptor implements FactoryBean { + private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder + .getContextHolderStrategy(); + private final PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); @Override public AuthorizationManagerAfterMethodInterceptor getObject() { - return AuthorizationManagerAfterMethodInterceptor.postAuthorize(this.manager); + AuthorizationManagerAfterMethodInterceptor interceptor = AuthorizationManagerAfterMethodInterceptor + .postAuthorize(this.manager); + interceptor.setSecurityContextHolderStrategy(this.securityContextHolderStrategy); + return interceptor; } @Override @@ -219,10 +268,28 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser return AuthorizationManagerAfterMethodInterceptor.class; } + public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) { + this.securityContextHolderStrategy = securityContextHolderStrategy; + } + public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) { this.manager.setExpressionHandler(expressionHandler); } } + 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/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 c2c7812b5e..0860cf217a 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 @@ -211,6 +211,9 @@ method-security.attlist &= method-security.attlist &= ## If true, class-based proxying will be used instead of interface-based proxying. attribute proxy-target-class {xsd:boolean}? +method-security.attlist &= + ## Specifies the security context holder strategy to use, by default uses a ThreadLocal-based strategy + attribute security-context-holder-strategy-ref {xsd:string}? global-method-security = ## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of "protect-pointcut" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie "protect-pointcut" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250. 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 38ed40b300..c58cf3e45b 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 @@ -651,6 +651,13 @@ + + + Specifies the security context holder strategy to use, by default uses a ThreadLocal-based + strategy + + + diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-6.0.rnc b/config/src/main/resources/org/springframework/security/config/spring-security-6.0.rnc index db78e82c19..53fcb14a92 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-6.0.rnc +++ b/config/src/main/resources/org/springframework/security/config/spring-security-6.0.rnc @@ -211,6 +211,9 @@ method-security.attlist &= method-security.attlist &= ## If true, class-based proxying will be used instead of interface-based proxying. attribute proxy-target-class {xsd:boolean}? +method-security.attlist &= + ## Specifies the security context holder strategy to use, by default uses a ThreadLocal-based strategy + attribute security-context-holder-strategy-ref {xsd:string}? global-method-security = ## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of "protect-pointcut" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie "protect-pointcut" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250. diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-6.0.xsd b/config/src/main/resources/org/springframework/security/config/spring-security-6.0.xsd index 3bf6300e88..d8fd977943 100644 --- a/config/src/main/resources/org/springframework/security/config/spring-security-6.0.xsd +++ b/config/src/main/resources/org/springframework/security/config/spring-security-6.0.xsd @@ -651,6 +651,13 @@ + + + Specifies the security context holder strategy to use, by default uses a ThreadLocal-based + strategy + + + diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.java index 3638e84485..1411eb92f6 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.java +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityService.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import java.util.List; import jakarta.annotation.security.DenyAll; import jakarta.annotation.security.PermitAll; +import jakarta.annotation.security.RolesAllowed; import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.prepost.PostAuthorize; @@ -49,6 +50,9 @@ public interface MethodSecurityService { @PermitAll String jsr250PermitAll(); + @RolesAllowed("ADMIN") + String jsr250RolesAllowed(); + @Secured({ "ROLE_USER", "RUN_AS_SUPER" }) Authentication runAs(); @@ -73,6 +77,12 @@ public interface MethodSecurityService { @PostAuthorize("#o?.contains('grant')") String postAnnotation(@P("o") String object); + @PreFilter("filterObject == authentication.name") + List preFilterByUsername(List array); + + @PostFilter("filterObject == authentication.name") + List postFilterByUsername(List array); + @PreFilter("filterObject.length > 3") @PreAuthorize("hasRole('ADMIN')") @Secured("ROLE_USER") diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.java index cab0631af7..18a2b7eb59 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.java +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,6 +51,11 @@ public class MethodSecurityServiceImpl implements MethodSecurityService { return null; } + @Override + public String jsr250RolesAllowed() { + return null; + } + @Override public Authentication runAs() { return SecurityContextHolder.getContext().getAuthentication(); @@ -88,6 +93,16 @@ public class MethodSecurityServiceImpl implements MethodSecurityService { return null; } + @Override + public List preFilterByUsername(List array) { + return array; + } + + @Override + public List postFilterByUsername(List array) { + return array; + } + @Override public List manyAnnotations(List object) { return object; diff --git a/config/src/test/java/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests.java index adcb526a72..ea4b60c5ad 100644 --- a/config/src/test/java/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests.java @@ -34,6 +34,7 @@ import org.springframework.lang.Nullable; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.access.annotation.BusinessService; +import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; @@ -41,7 +42,10 @@ import org.springframework.security.config.annotation.method.configuration.Metho 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.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.context.SecurityContextHolderStrategy; +import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners; import org.springframework.security.test.context.support.WithAnonymousUser; import org.springframework.security.test.context.support.WithMockUser; @@ -49,6 +53,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.Mockito.verify; /** * @author Josh Cummings @@ -117,6 +122,17 @@ public class MethodSecurityBeanDefinitionParserTests { assertThat(result).isNull(); } + @Test + public void securedWhenCustomSecurityContextHolderStrategyThenUses() { + this.spring.configLocations(xml("MethodSecurityServiceEnabledCustomSecurityContextHolderStrategy")).autowire(); + SecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class); + SecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken("user", "pass")); + strategy.setContext(context); + assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured) + .withMessage("Access Denied"); + verify(strategy).getContext(); + } + @WithMockUser(roles = "ADMIN") @Test public void securedUserWhenRoleAdminThenAccessDeniedException() { @@ -148,6 +164,17 @@ public class MethodSecurityBeanDefinitionParserTests { this.methodSecurityService.preAuthorizeAdmin(); } + @Test + public void preAuthorizeWhenCustomSecurityContextHolderStrategyThenUses() { + this.spring.configLocations(xml("MethodSecurityServiceEnabledCustomSecurityContextHolderStrategy")).autowire(); + SecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class); + SecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken("user", "pass")); + strategy.setContext(context); + assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorizeAdmin) + .withMessage("Access Denied"); + verify(strategy).getContext(); + } + @WithMockUser(authorities = "PREFIX_ADMIN") @Test public void preAuthorizeAdminWhenRoleAdminAndCustomPrefixThenPasses() { @@ -187,6 +214,30 @@ public class MethodSecurityBeanDefinitionParserTests { assertThat(result).isNull(); } + @Test + public void preFilterWhenCustomSecurityContextHolderStrategyThenUses() { + this.spring.configLocations(xml("MethodSecurityServiceEnabledCustomSecurityContextHolderStrategy")).autowire(); + SecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class); + SecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken("user", "pass")); + strategy.setContext(context); + List result = this.methodSecurityService + .preFilterByUsername(new ArrayList<>(Arrays.asList("user", "bob", "joe"))); + assertThat(result).containsExactly("user"); + verify(strategy).getContext(); + } + + @Test + public void postFilterWhenCustomSecurityContextHolderStrategyThenUses() { + this.spring.configLocations(xml("MethodSecurityServiceEnabledCustomSecurityContextHolderStrategy")).autowire(); + SecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class); + SecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken("user", "pass")); + strategy.setContext(context); + List result = this.methodSecurityService + .postFilterByUsername(new ArrayList<>(Arrays.asList("user", "bob", "joe"))); + assertThat(result).containsExactly("user"); + verify(strategy).getContext(); + } + @WithMockUser("bob") @Test public void methodReturningAListWhenPrePostFiltersConfiguredThenFiltersList() { @@ -253,6 +304,17 @@ public class MethodSecurityBeanDefinitionParserTests { .withMessage("Access Denied"); } + @Test + public void jsr250WhenCustomSecurityContextHolderStrategyThenUses() { + this.spring.configLocations(xml("MethodSecurityServiceEnabledCustomSecurityContextHolderStrategy")).autowire(); + SecurityContextHolderStrategy strategy = this.spring.getContext().getBean(SecurityContextHolderStrategy.class); + SecurityContext context = new SecurityContextImpl(new TestingAuthenticationToken("user", "pass")); + strategy.setContext(context); + assertThatExceptionOfType(AccessDeniedException.class) + .isThrownBy(this.methodSecurityService::jsr250RolesAllowed).withMessage("Access Denied"); + verify(strategy).getContext(); + } + @WithAnonymousUser @Test public void jsr250PermitAllWhenRoleAnonymousThenPasses() { diff --git a/config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-MethodSecurityServiceEnabledCustomSecurityContextHolderStrategy.xml b/config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-MethodSecurityServiceEnabledCustomSecurityContextHolderStrategy.xml new file mode 100644 index 0000000000..66f088a255 --- /dev/null +++ b/config/src/test/resources/org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests-MethodSecurityServiceEnabledCustomSecurityContextHolderStrategy.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + diff --git a/docs/modules/ROOT/pages/servlet/appendix/namespace/method-security.adoc b/docs/modules/ROOT/pages/servlet/appendix/namespace/method-security.adoc index 3d50d5507c..aba3e89846 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/namespace/method-security.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/namespace/method-security.adoc @@ -28,6 +28,11 @@ Defaults to "false". If true, class based proxying will be used instead of interface based proxying. Defaults to "false". +[[nsa-method-security-security-context-holder-strategy-ref]] +* **security-context-holder-strategy-ref** +Specifies a SecurityContextHolderStrategy to use when retrieving the SecurityContext. +Defaults to the value returned by SecurityContextHolder.getContextHolderStrategy(). + [[nsa-method-security-children]] === Child Elements of