Add SecurityContextHolderStrategy XML Configuration for Method Security
Issue gh-11061
This commit is contained in:
parent
da57bac061
commit
9cd7c7b046
|
@ -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<MethodSecurityExpressionHandler>, ApplicationContextAware {
|
||||
|
||||
|
@ -158,11 +181,17 @@ public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser
|
|||
public static final class Jsr250AuthorizationMethodInterceptor
|
||||
implements FactoryBean<AuthorizationManagerBeforeMethodInterceptor>, 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<AuthorizationManagerBeforeMethodInterceptor> {
|
||||
|
||||
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<AuthorizationManagerAfterMethodInterceptor> {
|
||||
|
||||
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<SecurityContextHolderStrategy> {
|
||||
|
||||
@Override
|
||||
public SecurityContextHolderStrategy getObject() throws Exception {
|
||||
return SecurityContextHolder.getContextHolderStrategy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return SecurityContextHolderStrategy.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -124,7 +124,7 @@
|
|||
</xs:annotation>
|
||||
<xs:complexType/>
|
||||
</xs:element>
|
||||
|
||||
|
||||
<xs:attributeGroup name="password-encoder.attlist">
|
||||
<xs:attribute name="ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -408,7 +408,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="ldap-ap.attlist">
|
||||
<xs:attribute name="server-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -488,7 +488,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="password-compare.attlist">
|
||||
<xs:attribute name="password-attribute" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -541,7 +541,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="protect.attlist">
|
||||
<xs:attribute name="method" use="required" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -651,6 +651,13 @@
|
|||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="security-context-holder-strategy-ref" type="xs:string">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Specifies the security context holder strategy to use, by default uses a ThreadLocal-based
|
||||
strategy
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
<xs:element name="global-method-security">
|
||||
<xs:annotation>
|
||||
|
@ -842,13 +849,13 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<xs:attributeGroup name="protect-pointcut.attlist">
|
||||
<xs:attribute name="expression" use="required" type="xs:string">
|
||||
<xs:annotation>
|
||||
|
@ -1294,20 +1301,18 @@
|
|||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="use-authorization-manager" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which
|
||||
should be used for authorizing HTTP requests.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="authorization-manager-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which
|
||||
should be used for authorizing HTTP requests.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="use-authorization-manager" type="xs:boolean">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Use AuthorizationManager API instead of SecurityMetadataSource
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="authorization-manager-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Use this AuthorizationManager instead of deriving one from <intercept-url> elements
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="access-decision-manager-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Optional attribute specifying the ID of the AccessDecisionManager implementation which
|
||||
|
@ -1356,7 +1361,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="access-denied-handler.attlist">
|
||||
<xs:attribute name="ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -1381,7 +1386,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="intercept-url.attlist">
|
||||
<xs:attribute name="pattern" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -1438,7 +1443,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="logout.attlist">
|
||||
<xs:attribute name="logout-url" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -1485,7 +1490,7 @@
|
|||
<xs:attributeGroup ref="security:ref"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
|
||||
|
||||
<xs:attributeGroup name="form-login.attlist">
|
||||
<xs:attribute name="login-processing-url" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -2000,7 +2005,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:element name="attribute-exchange">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Sets up an attribute exchange configuration to request specified attributes from the
|
||||
|
@ -2067,7 +2072,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="saml2-login.attlist">
|
||||
<xs:attribute name="relying-party-registration-repository-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -2124,7 +2129,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="saml2-logout.attlist">
|
||||
<xs:attribute name="logout-url" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -2577,7 +2582,7 @@
|
|||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="http-basic.attlist">
|
||||
<xs:attribute name="entry-point-ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -2610,7 +2615,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="session-management.attlist">
|
||||
<xs:attribute name="session-fixation-protection">
|
||||
<xs:annotation>
|
||||
|
@ -2666,7 +2671,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="concurrency-control.attlist">
|
||||
<xs:attribute name="max-sessions" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -2713,7 +2718,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="remember-me.attlist">
|
||||
<xs:attribute name="key" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -2811,7 +2816,7 @@
|
|||
<xs:attributeGroup name="remember-me-data-source-ref">
|
||||
<xs:attributeGroup ref="security:data-source-ref"/>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="anonymous.attlist">
|
||||
<xs:attribute name="key" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -2844,8 +2849,8 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
|
||||
|
||||
<xs:attributeGroup name="http-port">
|
||||
<xs:attribute name="http" use="required" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -2862,7 +2867,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="x509.attlist">
|
||||
<xs:attribute name="subject-principal-regex" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -2999,7 +3004,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="ap.attlist">
|
||||
<xs:attribute name="ref" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -3051,7 +3056,7 @@
|
|||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
|
||||
<xs:attributeGroup name="user.attlist">
|
||||
<xs:attribute name="name" use="required" type="xs:token">
|
||||
<xs:annotation>
|
||||
|
@ -3794,4 +3799,4 @@
|
|||
<xs:enumeration value="LAST"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|
||||
</xs:schema>
|
|
@ -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 javax.annotation.security.DenyAll;
|
||||
import javax.annotation.security.PermitAll;
|
||||
import javax.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<String> preFilterByUsername(List<String> array);
|
||||
|
||||
@PostFilter("filterObject == authentication.name")
|
||||
List<String> postFilterByUsername(List<String> array);
|
||||
|
||||
@PreFilter("filterObject.length > 3")
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@Secured("ROLE_USER")
|
||||
|
|
|
@ -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<String> preFilterByUsername(List<String> array) {
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> postFilterByUsername(List<String> array) {
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> manyAnnotations(List<String> object) {
|
||||
return object;
|
||||
|
|
|
@ -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<String> 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<String> 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() {
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ https://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/security"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<method-security secured-enabled="true" jsr250-enabled="true" security-context-holder-strategy-ref="ref"/>
|
||||
|
||||
<b:bean id="ref" class="org.mockito.Mockito" factory-method="spy">
|
||||
<b:constructor-arg>
|
||||
<b:bean class="org.springframework.security.config.MockSecurityContextHolderStrategy"/>
|
||||
</b:constructor-arg>
|
||||
</b:bean>
|
||||
|
||||
<b:bean class="org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl"/>
|
||||
|
||||
</b:beans>
|
|
@ -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 <method-security>
|
||||
|
||||
|
|
Loading…
Reference in New Issue