Polish AuthorizationManager Method Security

- Removed consolidated pointcut advisor in favor of each interceptor
being an advisor. This allows Spring AOP to do more of the heavy
lifting of selecting the set of interceptors that applies
- Created new method context for after interceptors instead of
modifying existing one
- Added documentation
- Added XML support
- Added AuthorizationInterceptorsOrder to simplify interceptor
ordering
- Adjusted annotation lookup to comply with JSR-250 spec
- Adjusted annotation lookup to exhaustively search for duplicate
annotations
- Separated into three @Configuration classes, one for each set of
authorization annotations

Issue gh-9289
This commit is contained in:
Josh Cummings 2021-04-07 15:33:47 -06:00
parent 84e2e80915
commit 67e5c05a47
72 changed files with 4510 additions and 2342 deletions

View File

@ -87,6 +87,8 @@ public abstract class Elements {
public static final String GLOBAL_METHOD_SECURITY = "global-method-security";
public static final String METHOD_SECURITY = "method-security";
public static final String PASSWORD_ENCODER = "password-encoder";
public static final String PORT_MAPPINGS = "port-mappings";

View File

@ -44,6 +44,7 @@ import org.springframework.security.config.ldap.LdapServerBeanDefinitionParser;
import org.springframework.security.config.ldap.LdapUserServiceBeanDefinitionParser;
import org.springframework.security.config.method.GlobalMethodSecurityBeanDefinitionParser;
import org.springframework.security.config.method.InterceptMethodsBeanDefinitionDecorator;
import org.springframework.security.config.method.MethodSecurityBeanDefinitionParser;
import org.springframework.security.config.method.MethodSecurityMetadataSourceBeanDefinitionParser;
import org.springframework.security.config.oauth2.client.ClientRegistrationsBeanDefinitionParser;
import org.springframework.security.config.websocket.WebSocketMessageBrokerSecurityBeanDefinitionParser;
@ -169,6 +170,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
this.parsers.put(Elements.JDBC_USER_SERVICE, new JdbcUserServiceBeanDefinitionParser());
this.parsers.put(Elements.AUTHENTICATION_PROVIDER, new AuthenticationProviderBeanDefinitionParser());
this.parsers.put(Elements.GLOBAL_METHOD_SECURITY, new GlobalMethodSecurityBeanDefinitionParser());
this.parsers.put(Elements.METHOD_SECURITY, new MethodSecurityBeanDefinitionParser());
this.parsers.put(Elements.AUTHENTICATION_MANAGER, new AuthenticationManagerBeanDefinitionParser());
this.parsers.put(Elements.METHOD_SECURITY_METADATA_SOURCE,
new MethodSecurityMetadataSourceBeanDefinitionParser());

View File

@ -25,13 +25,17 @@ import java.lang.annotation.Target;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreFilter;
/**
* Enables Spring Security Method Security.
* @author Evgeniy Cheban
* @since 5.5
* @author Josh Cummings
* @since 5.6
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@ -40,6 +44,14 @@ import org.springframework.security.access.annotation.Secured;
@Configuration
public @interface EnableMethodSecurity {
/**
* Determines if Spring Security's {@link PreAuthorize}, {@link PostAuthorize},
* {@link PreFilter}, and {@link PostFilter} annotations should be enabled. Default is
* true.
* @return true if pre/post annotations should be enabled false otherwise
*/
boolean prePostEnabled() default true;
/**
* Determines if Spring Security's {@link Secured} annotation should be enabled.
* Default is false.
@ -76,12 +88,4 @@ public @interface EnableMethodSecurity {
*/
AdviceMode mode() default AdviceMode.PROXY;
/**
* Indicate the ordering of the execution of the security advisor when multiple
* advices are applied at a specific joinpoint. The default is
* {@link Ordered#LOWEST_PRECEDENCE}.
* @return the order the security advisor should be applied
*/
int order() default Ordered.LOWEST_PRECEDENCE;
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.config.annotation.method.configuration;
import org.springframework.aop.Advisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.authorization.method.Jsr250AuthorizationManager;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
/**
* {@link Configuration} for enabling JSR-250 Spring Security Method Security.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @see EnableMethodSecurity
* @since 5.6
*/
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
final class Jsr250MethodSecurityConfiguration {
private final Jsr250AuthorizationManager jsr250AuthorizationManager = new Jsr250AuthorizationManager();
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor jsr250AuthorizationMethodInterceptor() {
return AuthorizationManagerBeforeMethodInterceptor.jsr250(this.jsr250AuthorizationManager);
}
@Autowired(required = false)
void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) {
this.jsr250AuthorizationManager.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
}
}

View File

@ -1,252 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.Pointcuts;
import org.springframework.aop.support.StaticMethodMatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.context.annotation.Role;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.access.annotation.Jsr250AuthorizationManager;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.annotation.SecuredAuthorizationManager;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.aopalliance.AuthorizationMethodInterceptor;
import org.springframework.security.access.method.AuthorizationManagerMethodAfterAdvice;
import org.springframework.security.access.method.AuthorizationManagerMethodBeforeAdvice;
import org.springframework.security.access.method.AuthorizationMethodAfterAdvice;
import org.springframework.security.access.method.AuthorizationMethodBeforeAdvice;
import org.springframework.security.access.method.DelegatingAuthorizationMethodAfterAdvice;
import org.springframework.security.access.method.DelegatingAuthorizationMethodBeforeAdvice;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodAfterAdvice;
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodBeforeAdvice;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
/**
* Base {@link Configuration} for enabling Spring Security Method Security.
*
* @author Evgeniy Cheban
* @see EnableMethodSecurity
* @since 5.5
*/
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
final class MethodSecurityConfiguration implements ImportAware {
private MethodSecurityExpressionHandler methodSecurityExpressionHandler;
private GrantedAuthorityDefaults grantedAuthorityDefaults;
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> authorizationMethodBeforeAdvice;
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> authorizationMethodAfterAdvice;
private AnnotationAttributes enableMethodSecurity;
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
DefaultPointcutAdvisor methodSecurityAdvisor(AuthorizationMethodInterceptor interceptor) {
Pointcut pointcut = Pointcuts.union(getAuthorizationMethodBeforeAdvice(), getAuthorizationMethodAfterAdvice());
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, interceptor);
advisor.setOrder(order());
return advisor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
AuthorizationMethodInterceptor authorizationMethodInterceptor() {
return new AuthorizationMethodInterceptor(getAuthorizationMethodBeforeAdvice(),
getAuthorizationMethodAfterAdvice());
}
private MethodSecurityExpressionHandler getMethodSecurityExpressionHandler() {
if (this.methodSecurityExpressionHandler == null) {
this.methodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
}
return this.methodSecurityExpressionHandler;
}
@Autowired(required = false)
void setMethodSecurityExpressionHandler(MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
this.methodSecurityExpressionHandler = methodSecurityExpressionHandler;
}
@Autowired(required = false)
void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) {
this.grantedAuthorityDefaults = grantedAuthorityDefaults;
}
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> getAuthorizationMethodBeforeAdvice() {
if (this.authorizationMethodBeforeAdvice == null) {
this.authorizationMethodBeforeAdvice = createDefaultAuthorizationMethodBeforeAdvice();
}
return this.authorizationMethodBeforeAdvice;
}
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> createDefaultAuthorizationMethodBeforeAdvice() {
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> beforeAdvices = new ArrayList<>();
beforeAdvices.add(getPreFilterAuthorizationMethodBeforeAdvice());
beforeAdvices.add(getPreAuthorizeAuthorizationMethodBeforeAdvice());
if (securedEnabled()) {
beforeAdvices.add(getSecuredAuthorizationMethodBeforeAdvice());
}
if (jsr250Enabled()) {
beforeAdvices.add(getJsr250AuthorizationMethodBeforeAdvice());
}
return new DelegatingAuthorizationMethodBeforeAdvice(beforeAdvices);
}
private PreFilterAuthorizationMethodBeforeAdvice getPreFilterAuthorizationMethodBeforeAdvice() {
PreFilterAuthorizationMethodBeforeAdvice preFilterBeforeAdvice = new PreFilterAuthorizationMethodBeforeAdvice();
preFilterBeforeAdvice.setExpressionHandler(getMethodSecurityExpressionHandler());
return preFilterBeforeAdvice;
}
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> getPreAuthorizeAuthorizationMethodBeforeAdvice() {
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(PreAuthorize.class);
PreAuthorizeAuthorizationManager authorizationManager = new PreAuthorizeAuthorizationManager();
authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler());
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager);
}
private AuthorizationManagerMethodBeforeAdvice<MethodAuthorizationContext> getSecuredAuthorizationMethodBeforeAdvice() {
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(Secured.class);
SecuredAuthorizationManager authorizationManager = new SecuredAuthorizationManager();
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager);
}
private AuthorizationManagerMethodBeforeAdvice<MethodAuthorizationContext> getJsr250AuthorizationMethodBeforeAdvice() {
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(DenyAll.class, PermitAll.class,
RolesAllowed.class);
Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager();
if (this.grantedAuthorityDefaults != null) {
authorizationManager.setRolePrefix(this.grantedAuthorityDefaults.getRolePrefix());
}
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager);
}
@Autowired(required = false)
void setAuthorizationMethodBeforeAdvice(
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> authorizationMethodBeforeAdvice) {
this.authorizationMethodBeforeAdvice = authorizationMethodBeforeAdvice;
}
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> getAuthorizationMethodAfterAdvice() {
if (this.authorizationMethodAfterAdvice == null) {
this.authorizationMethodAfterAdvice = createDefaultAuthorizationMethodAfterAdvice();
}
return this.authorizationMethodAfterAdvice;
}
private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> createDefaultAuthorizationMethodAfterAdvice() {
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> afterAdvices = new ArrayList<>();
afterAdvices.add(getPostFilterAuthorizationMethodAfterAdvice());
afterAdvices.add(getPostAuthorizeAuthorizationMethodAfterAdvice());
return new DelegatingAuthorizationMethodAfterAdvice(afterAdvices);
}
private PostFilterAuthorizationMethodAfterAdvice getPostFilterAuthorizationMethodAfterAdvice() {
PostFilterAuthorizationMethodAfterAdvice postFilterAfterAdvice = new PostFilterAuthorizationMethodAfterAdvice();
postFilterAfterAdvice.setExpressionHandler(getMethodSecurityExpressionHandler());
return postFilterAfterAdvice;
}
private AuthorizationManagerMethodAfterAdvice<MethodAuthorizationContext> getPostAuthorizeAuthorizationMethodAfterAdvice() {
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(PostAuthorize.class);
PostAuthorizeAuthorizationManager authorizationManager = new PostAuthorizeAuthorizationManager();
authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler());
return new AuthorizationManagerMethodAfterAdvice<>(methodMatcher, authorizationManager);
}
@Autowired(required = false)
void setAuthorizationMethodAfterAdvice(
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> authorizationMethodAfterAdvice) {
this.authorizationMethodAfterAdvice = authorizationMethodAfterAdvice;
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> attributes = importMetadata.getAnnotationAttributes(EnableMethodSecurity.class.getName());
this.enableMethodSecurity = AnnotationAttributes.fromMap(attributes);
}
private boolean securedEnabled() {
return this.enableMethodSecurity.getBoolean("securedEnabled");
}
private boolean jsr250Enabled() {
return this.enableMethodSecurity.getBoolean("jsr250Enabled");
}
private int order() {
return this.enableMethodSecurity.getNumber("order");
}
private static final class SecurityAnnotationsStaticMethodMatcher extends StaticMethodMatcher {
private final Set<Class<? extends Annotation>> annotationClasses;
@SafeVarargs
private SecurityAnnotationsStaticMethodMatcher(Class<? extends Annotation>... annotationClasses) {
this.annotationClasses = new HashSet<>(Arrays.asList(annotationClasses));
}
@Override
public boolean matches(Method method, Class<?> targetClass) {
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
return hasAnnotations(specificMethod) || hasAnnotations(specificMethod.getDeclaringClass());
}
private boolean hasAnnotations(AnnotatedElement annotatedElement) {
Set<Annotation> annotations = AnnotatedElementUtils.findAllMergedAnnotations(annotatedElement,
this.annotationClasses);
return !annotations.isEmpty();
}
}
}

View File

@ -17,34 +17,59 @@
package org.springframework.security.config.annotation.method.configuration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.context.annotation.AutoProxyRegistrar;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.NonNull;
/**
* Dynamically determines which imports to include using the {@link EnableMethodSecurity}
* annotation.
*
* @author Evgeniy Cheban
* @since 5.5
* @author Josh Cummings
* @since 5.6
*/
final class MethodSecuritySelector extends AdviceModeImportSelector<EnableMethodSecurity> {
final class MethodSecuritySelector implements ImportSelector {
private final ImportSelector autoProxy = new AutoProxyRegistrarSelector();
@Override
protected String[] selectImports(AdviceMode adviceMode) {
if (adviceMode == AdviceMode.PROXY) {
return getProxyImports();
public String[] selectImports(@NonNull AnnotationMetadata importMetadata) {
if (!importMetadata.hasAnnotation(EnableMethodSecurity.class.getName())) {
return new String[0];
}
throw new IllegalStateException("AdviceMode '" + adviceMode + "' is not supported");
EnableMethodSecurity annotation = importMetadata.getAnnotations().get(EnableMethodSecurity.class).synthesize();
List<String> imports = new ArrayList<>(Arrays.asList(this.autoProxy.selectImports(importMetadata)));
if (annotation.prePostEnabled()) {
imports.add(PrePostMethodSecurityConfiguration.class.getName());
}
if (annotation.securedEnabled()) {
imports.add(SecuredMethodSecurityConfiguration.class.getName());
}
if (annotation.jsr250Enabled()) {
imports.add(Jsr250MethodSecurityConfiguration.class.getName());
}
return imports.toArray(new String[0]);
}
private String[] getProxyImports() {
List<String> result = new ArrayList<>();
result.add(AutoProxyRegistrar.class.getName());
result.add(MethodSecurityConfiguration.class.getName());
return result.toArray(new String[0]);
private static final class AutoProxyRegistrarSelector extends AdviceModeImportSelector<EnableMethodSecurity> {
private static final String[] IMPORTS = new String[] { AutoProxyRegistrar.class.getName() };
@Override
protected String[] selectImports(@NonNull AdviceMode adviceMode) {
if (adviceMode == AdviceMode.PROXY) {
return IMPORTS;
}
throw new IllegalStateException("AdviceMode '" + adviceMode + "' is not supported");
}
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.config.annotation.method.configuration;
import org.springframework.aop.Advisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
/**
* Base {@link Configuration} for enabling Spring Security Method Security.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @see EnableMethodSecurity
* @since 5.6
*/
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
final class PrePostMethodSecurityConfiguration {
private final PreFilterAuthorizationMethodInterceptor preFilterAuthorizationMethodInterceptor = new PreFilterAuthorizationMethodInterceptor();
private final PreAuthorizeAuthorizationManager preAuthorizeAuthorizationManager = new PreAuthorizeAuthorizationManager();
private final PostAuthorizeAuthorizationManager postAuthorizeAuthorizationManager = new PostAuthorizeAuthorizationManager();
private final PostFilterAuthorizationMethodInterceptor postFilterAuthorizationMethodInterceptor = new PostFilterAuthorizationMethodInterceptor();
private boolean customMethodSecurityExpressionHandler = false;
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor preFilterAuthorizationMethodInterceptor() {
return this.preFilterAuthorizationMethodInterceptor;
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor preAuthorizeAuthorizationMethodInterceptor() {
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(this.preAuthorizeAuthorizationManager);
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor postAuthorizeAuthorizationMethodInterceptor() {
return AuthorizationManagerAfterMethodInterceptor.postAuthorize(this.postAuthorizeAuthorizationManager);
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor postFilterAuthorizationMethodInterceptor() {
return this.postFilterAuthorizationMethodInterceptor;
}
@Autowired(required = false)
void setMethodSecurityExpressionHandler(MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
this.customMethodSecurityExpressionHandler = true;
this.preFilterAuthorizationMethodInterceptor.setExpressionHandler(methodSecurityExpressionHandler);
this.preAuthorizeAuthorizationManager.setExpressionHandler(methodSecurityExpressionHandler);
this.postAuthorizeAuthorizationManager.setExpressionHandler(methodSecurityExpressionHandler);
this.postFilterAuthorizationMethodInterceptor.setExpressionHandler(methodSecurityExpressionHandler);
}
@Autowired(required = false)
void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) {
if (this.customMethodSecurityExpressionHandler) {
return;
}
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
this.preFilterAuthorizationMethodInterceptor.setExpressionHandler(expressionHandler);
this.preAuthorizeAuthorizationManager.setExpressionHandler(expressionHandler);
this.postAuthorizeAuthorizationManager.setExpressionHandler(expressionHandler);
this.postFilterAuthorizationMethodInterceptor.setExpressionHandler(expressionHandler);
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.config.annotation.method.configuration;
import org.springframework.aop.Advisor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
/**
* {@link Configuration} for enabling {@link Secured} Spring Security Method Security.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @see EnableMethodSecurity
* @since 5.6
*/
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
final class SecuredMethodSecurityConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor securedAuthorizationMethodInterceptor() {
return AuthorizationManagerBeforeMethodInterceptor.secured();
}
}

View File

@ -0,0 +1,228 @@
/*
* Copyright 2002-2018 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.
*/
package org.springframework.security.config.method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;
import org.springframework.aop.config.AopNamespaceUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.authorization.method.Jsr250AuthorizationManager;
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;
import org.springframework.security.config.Elements;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.util.xml.DomUtils;
/**
* Processes the top-level "method-security" element.
*
* @author Josh Cummings
* @since 5.6
*/
public class MethodSecurityBeanDefinitionParser implements BeanDefinitionParser {
private final Log logger = LogFactory.getLog(getClass());
private static final String ATT_USE_JSR250 = "jsr250-enabled";
private static final String ATT_USE_SECURED = "secured-enabled";
private static final String ATT_USE_PREPOST = "pre-post-enabled";
private static final String ATT_REF = "ref";
@Override
public BeanDefinition parse(Element element, ParserContext pc) {
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),
pc.extractSource(element));
pc.pushContainingComponent(compositeDef);
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);
BeanDefinitionBuilder preAuthorizeInterceptor = BeanDefinitionBuilder
.rootBeanDefinition(PreAuthorizeAuthorizationMethodInterceptor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
BeanDefinitionBuilder postAuthorizeInterceptor = BeanDefinitionBuilder
.rootBeanDefinition(PostAuthorizeAuthorizationMethodInterceptor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
BeanDefinitionBuilder postFilterInterceptor = BeanDefinitionBuilder
.rootBeanDefinition(PostFilterAuthorizationMethodInterceptor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
Element expressionHandlerElt = DomUtils.getChildElementByTagName(element, Elements.EXPRESSION_HANDLER);
if (expressionHandlerElt != null) {
String expressionHandlerRef = expressionHandlerElt.getAttribute(ATT_REF);
preFilterInterceptor.addPropertyReference("expressionHandler", expressionHandlerRef);
preAuthorizeInterceptor.addPropertyReference("expressionHandler", expressionHandlerRef);
postAuthorizeInterceptor.addPropertyReference("expressionHandler", expressionHandlerRef);
postFilterInterceptor.addPropertyReference("expressionHandler", expressionHandlerRef);
}
else {
BeanDefinition expressionHandler = BeanDefinitionBuilder
.rootBeanDefinition(MethodSecurityExpressionHandlerBean.class).getBeanDefinition();
preFilterInterceptor.addPropertyValue("expressionHandler", expressionHandler);
preAuthorizeInterceptor.addPropertyValue("expressionHandler", expressionHandler);
postAuthorizeInterceptor.addPropertyValue("expressionHandler", expressionHandler);
postFilterInterceptor.addPropertyValue("expressionHandler", expressionHandler);
}
pc.getRegistry().registerBeanDefinition("preFilterAuthorizationMethodInterceptor",
preFilterInterceptor.getBeanDefinition());
pc.getRegistry().registerBeanDefinition("preAuthorizeAuthorizationMethodInterceptor",
preAuthorizeInterceptor.getBeanDefinition());
pc.getRegistry().registerBeanDefinition("postAuthorizeAuthorizationMethodInterceptor",
postAuthorizeInterceptor.getBeanDefinition());
pc.getRegistry().registerBeanDefinition("postFilterAuthorizationMethodInterceptor",
postFilterInterceptor.getBeanDefinition());
}
boolean securedEnabled = "true".equals(element.getAttribute(ATT_USE_SECURED));
if (securedEnabled) {
BeanDefinitionBuilder securedInterceptor = BeanDefinitionBuilder
.rootBeanDefinition(AuthorizationManagerBeforeMethodInterceptor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE).setFactoryMethod("secured");
pc.getRegistry().registerBeanDefinition("securedAuthorizationMethodInterceptor",
securedInterceptor.getBeanDefinition());
}
boolean jsr250Enabled = "true".equals(element.getAttribute(ATT_USE_JSR250));
if (jsr250Enabled) {
BeanDefinitionBuilder jsr250Interceptor = BeanDefinitionBuilder
.rootBeanDefinition(Jsr250AuthorizationMethodInterceptor.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
pc.getRegistry().registerBeanDefinition("jsr250AuthorizationMethodInterceptor",
jsr250Interceptor.getBeanDefinition());
}
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(pc, element);
pc.popAndRegisterContainingComponent();
return null;
}
public static final class MethodSecurityExpressionHandlerBean
implements FactoryBean<MethodSecurityExpressionHandler>, ApplicationContextAware {
private final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
@Override
public MethodSecurityExpressionHandler getObject() {
return this.expressionHandler;
}
@Override
public Class<?> getObjectType() {
return MethodSecurityExpressionHandler.class;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
String[] grantedAuthorityDefaultsBeanNames = applicationContext
.getBeanNamesForType(GrantedAuthorityDefaults.class);
if (grantedAuthorityDefaultsBeanNames.length == 1) {
GrantedAuthorityDefaults grantedAuthorityDefaults = applicationContext
.getBean(grantedAuthorityDefaultsBeanNames[0], GrantedAuthorityDefaults.class);
this.expressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
}
}
}
public static final class Jsr250AuthorizationMethodInterceptor
implements FactoryBean<AuthorizationManagerBeforeMethodInterceptor>, ApplicationContextAware {
private final Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
@Override
public AuthorizationManagerBeforeMethodInterceptor getObject() {
return AuthorizationManagerBeforeMethodInterceptor.jsr250(this.manager);
}
@Override
public Class<?> getObjectType() {
return AuthorizationManagerBeforeMethodInterceptor.class;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
String[] grantedAuthorityDefaultsBeanNames = applicationContext
.getBeanNamesForType(GrantedAuthorityDefaults.class);
if (grantedAuthorityDefaultsBeanNames.length == 1) {
GrantedAuthorityDefaults grantedAuthorityDefaults = applicationContext
.getBean(grantedAuthorityDefaultsBeanNames[0], GrantedAuthorityDefaults.class);
this.manager.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
}
}
}
public static final class PreAuthorizeAuthorizationMethodInterceptor
implements FactoryBean<AuthorizationManagerBeforeMethodInterceptor> {
private final PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
@Override
public AuthorizationManagerBeforeMethodInterceptor getObject() {
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(this.manager);
}
@Override
public Class<?> getObjectType() {
return AuthorizationManagerBeforeMethodInterceptor.class;
}
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
this.manager.setExpressionHandler(expressionHandler);
}
}
public static final class PostAuthorizeAuthorizationMethodInterceptor
implements FactoryBean<AuthorizationManagerAfterMethodInterceptor> {
private final PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
@Override
public AuthorizationManagerAfterMethodInterceptor getObject() {
return AuthorizationManagerAfterMethodInterceptor.postAuthorize(this.manager);
}
@Override
public Class<?> getObjectType() {
return AuthorizationManagerAfterMethodInterceptor.class;
}
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
this.manager.setExpressionHandler(expressionHandler);
}
}
}

View File

@ -196,6 +196,22 @@ msmds.attlist &= id?
msmds.attlist &= use-expressions?
method-security =
## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with Spring Security annotations. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. Interceptors are invoked in the order specified in AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.
element method-security {method-security.attlist, expression-handler?}
method-security.attlist &=
## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to "true".
attribute pre-post-enabled {xsd:boolean}?
method-security.attlist &=
## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to "false".
attribute secured-enabled {xsd:boolean}?
method-security.attlist &=
## Specifies whether JSR-250 style attributes are to be used (for example "RolesAllowed"). This will require the javax.annotation.security classes on the classpath. Defaults to "false".
attribute jsr250-enabled {xsd:boolean}?
method-security.attlist &=
## If true, class-based proxying will be used instead of interface-based proxying.
attribute proxy-target-class {xsd:boolean}?
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.
element global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*}

View File

@ -595,6 +595,63 @@
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="method-security">
<xs:annotation>
<xs:documentation>Provides method security for all beans registered in the Spring application context.
Specifically, beans will be scanned for matches with Spring Security annotations. Where
there is a match, the beans will automatically be proxied and security authorization
applied to the methods accordingly. Interceptors are invoked in the order specified in
AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="expression-handler">
<xs:annotation>
<xs:documentation>Defines the SecurityExpressionHandler instance which will be used if expression-based
access-control is enabled. A default implementation (with no ACL support) will be used if
not supplied.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:attributeGroup ref="security:ref"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attributeGroup ref="security:method-security.attlist"/>
</xs:complexType>
</xs:element>
<xs:attributeGroup name="method-security.attlist">
<xs:attribute name="pre-post-enabled" type="xs:boolean">
<xs:annotation>
<xs:documentation>Specifies whether the use of Spring Security's pre and post invocation annotations
(@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this
application context. Defaults to "true".
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="secured-enabled" type="xs:boolean">
<xs:annotation>
<xs:documentation>Specifies whether the use of Spring Security's @Secured annotations should be enabled for
this application context. Defaults to "false".
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="jsr250-enabled" type="xs:boolean">
<xs:annotation>
<xs:documentation>Specifies whether JSR-250 style attributes are to be used (for example "RolesAllowed").
This will require the javax.annotation.security classes on the classpath. Defaults to
"false".
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="proxy-target-class" type="xs:boolean">
<xs:annotation>
<xs:documentation>If true, class-based proxying will be used instead of interface-based proxying.
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:attributeGroup>
<xs:element name="global-method-security">
<xs:annotation>
<xs:documentation>Provides method security for all beans registered in the Spring application context.

View File

@ -16,12 +16,16 @@
package org.springframework.security.config.annotation.method.configuration;
import java.util.List;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.access.prepost.PreFilter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.parameters.P;
@ -69,4 +73,15 @@ public interface MethodSecurityService {
@PostAuthorize("#o?.contains('grant')")
String postAnnotation(@P("o") String object);
@PreFilter("filterObject.length > 3")
@PreAuthorize("hasRole('ADMIN')")
@Secured("ROLE_USER")
@PostFilter("filterObject.length > 5")
@PostAuthorize("returnObject.size == 2")
List<String> manyAnnotations(List<String> array);
@RequireUserRole
@RequireAdminRole
void repeatedAnnotations();
}

View File

@ -16,6 +16,8 @@
package org.springframework.security.config.annotation.method.configuration;
import java.util.List;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
@ -86,4 +88,13 @@ public class MethodSecurityServiceImpl implements MethodSecurityService {
return null;
}
@Override
public List<String> manyAnnotations(List<String> object) {
return object;
}
@Override
public void repeatedAnnotations() {
}
}

View File

@ -18,31 +18,39 @@ package org.springframework.security.config.annotation.method.configuration;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Advisor;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.JdkRegexpMethodPointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Role;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.annotation.BusinessService;
import org.springframework.security.access.annotation.BusinessServiceImpl;
import org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl;
import org.springframework.security.access.annotation.Jsr250BusinessServiceImpl;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.method.AuthorizationManagerMethodBeforeAdvice;
import org.springframework.security.access.method.AuthorizationMethodAfterAdvice;
import org.springframework.security.access.method.AuthorizationMethodBeforeAdvice;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.method.AuthorizationInterceptorsOrder;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
import org.springframework.security.test.context.support.WithAnonymousUser;
import org.springframework.security.test.context.support.WithMockUser;
@ -52,13 +60,14 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for {@link MethodSecurityConfiguration}.
* Tests for {@link PrePostMethodSecurityConfiguration}.
*
* @author Evgeniy Cheban
* @author Josh Cummings
*/
@RunWith(SpringRunner.class)
@SecurityTestExecutionListeners
public class MethodSecurityConfigurationTests {
public class PrePostMethodSecurityConfigurationTests {
@Rule
public final SpringTestRule spring = new SpringTestRule();
@ -103,7 +112,7 @@ public class MethodSecurityConfigurationTests {
@WithMockUser
@Test
public void securedWhenRoleUserThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured)
.withMessage("Access Denied");
}
@ -119,7 +128,7 @@ public class MethodSecurityConfigurationTests {
@WithMockUser(roles = "ADMIN")
@Test
public void securedUserWhenRoleAdminThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)
.withMessage("Access Denied");
}
@ -147,6 +156,13 @@ public class MethodSecurityConfigurationTests {
this.methodSecurityService.preAuthorizeAdmin();
}
@WithMockUser(authorities = "PREFIX_ADMIN")
@Test
public void preAuthorizeAdminWhenRoleAdminAndCustomPrefixThenPasses() {
this.spring.register(CustomGrantedAuthorityDefaultsConfig.class, MethodSecurityServiceConfig.class).autowire();
this.methodSecurityService.preAuthorizeAdmin();
}
@WithMockUser
@Test
public void postHasPermissionWhenParameterIsNotGrantThenAccessDeniedException() {
@ -244,7 +260,7 @@ public class MethodSecurityConfigurationTests {
@WithMockUser(roles = "ADMIN")
@Test
public void jsr250WhenRoleAdminThenAccessDeniedException() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::jsr250)
.withMessage("Access Denied");
}
@ -252,7 +268,7 @@ public class MethodSecurityConfigurationTests {
@WithAnonymousUser
@Test
public void jsr250PermitAllWhenRoleAnonymousThenPasses() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
String result = this.methodSecurityService.jsr250PermitAll();
assertThat(result).isNull();
}
@ -272,7 +288,70 @@ public class MethodSecurityConfigurationTests {
this.businessService.rolesAllowedUser();
}
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
@WithMockUser(roles = { "ADMIN", "USER" })
@Test
public void manyAnnotationsWhenMeetsConditionsThenReturnsFilteredList() throws Exception {
List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo");
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
List<String> filtered = this.methodSecurityService.manyAnnotations(new ArrayList<>(names));
assertThat(filtered).hasSize(2);
assertThat(filtered).containsExactly("harold", "jonathan");
}
// gh-4003
// gh-4103
@WithMockUser
@Test
public void manyAnnotationsWhenUserThenFails() {
List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo");
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));
}
@WithMockUser
@Test
public void manyAnnotationsWhenShortListThenFails() {
List<String> names = Arrays.asList("harold", "jonathan", "pete");
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));
}
@WithMockUser(roles = "ADMIN")
@Test
public void manyAnnotationsWhenAdminThenFails() {
List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo");
this.spring.register(MethodSecurityServiceEnabledConfig.class).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));
}
// gh-3183
@Test
public void repeatedAnnotationsWhenPresentThenFails() {
this.spring.register(MethodSecurityServiceConfig.class).autowire();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> this.methodSecurityService.repeatedAnnotations());
}
// gh-3183
@Test
public void repeatedJsr250AnnotationsWhenPresentThenFails() {
this.spring.register(Jsr250Config.class).autowire();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> this.businessService.repeatedAnnotations());
}
// gh-3183
@Test
public void repeatedSecuredAnnotationsWhenPresentThenFails() {
this.spring.register(SecuredConfig.class).autowire();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> this.businessService.repeatedAnnotations());
}
@EnableMethodSecurity
static class MethodSecurityServiceConfig {
@Bean
@ -292,6 +371,36 @@ public class MethodSecurityConfigurationTests {
}
@EnableMethodSecurity(prePostEnabled = false, securedEnabled = true)
static class SecuredConfig {
@Bean
BusinessService businessService() {
return new BusinessServiceImpl<>();
}
}
@EnableMethodSecurity(prePostEnabled = false, jsr250Enabled = true)
static class Jsr250Config {
@Bean
BusinessService businessService() {
return new Jsr250BusinessServiceImpl();
}
}
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)
static class MethodSecurityServiceEnabledConfig {
@Bean
MethodSecurityService methodSecurityService() {
return new MethodSecurityServiceImpl();
}
}
@EnableMethodSecurity
static class CustomPermissionEvaluatorConfig {
@ -316,16 +425,27 @@ public class MethodSecurityConfigurationTests {
}
@EnableMethodSecurity
static class CustomGrantedAuthorityDefaultsConfig {
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("PREFIX_");
}
}
@EnableMethodSecurity
static class CustomAuthorizationManagerBeforeAdviceConfig {
@Bean
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> customBeforeAdvice() {
JdkRegexpMethodPointcut methodMatcher = new JdkRegexpMethodPointcut();
methodMatcher.setPattern(".*MethodSecurityServiceImpl.*securedUser");
AuthorizationManager<MethodAuthorizationContext> authorizationManager = (a,
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor customBeforeAdvice() {
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPattern(".*MethodSecurityServiceImpl.*securedUser");
AuthorizationManager<MethodInvocation> authorizationManager = (a,
o) -> new AuthorizationDecision("bob".equals(a.get().getName()));
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager);
return new AuthorizationManagerBeforeMethodInterceptor(pointcut, authorizationManager);
}
}
@ -334,25 +454,20 @@ public class MethodSecurityConfigurationTests {
static class CustomAuthorizationManagerAfterAdviceConfig {
@Bean
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> customAfterAdvice() {
JdkRegexpMethodPointcut methodMatcher = new JdkRegexpMethodPointcut();
methodMatcher.setPattern(".*MethodSecurityServiceImpl.*securedUser");
return new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
@Override
public MethodMatcher getMethodMatcher() {
return methodMatcher;
}
@Override
public Object after(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext, Object returnedObject) {
Authentication auth = authentication.get();
if ("bob".equals(auth.getName())) {
return "granted";
}
throw new AccessDeniedException("Access Denied for User '" + auth.getName() + "'");
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor customAfterAdvice() {
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
pointcut.setPattern(".*MethodSecurityServiceImpl.*securedUser");
MethodInterceptor interceptor = (mi) -> {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if ("bob".equals(auth.getName())) {
return "granted";
}
throw new AccessDeniedException("Access Denied for User '" + auth.getName() + "'");
};
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, interceptor);
advisor.setOrder(AuthorizationInterceptorsOrder.POST_FILTER.getOrder() + 1);
return advisor;
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.springframework.security.access.prepost.PreAuthorize;
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole('ADMIN')")
public @interface RequireAdminRole {
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.config.annotation.method.configuration;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.springframework.security.access.prepost.PreAuthorize;
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole('USER')")
public @interface RequireUserRole {
}

View File

@ -52,7 +52,6 @@ public class XsdDocumentedTests {
"nsa-authentication",
"nsa-websocket-security",
"nsa-ldap",
"nsa-method-security",
"nsa-web",
// deprecated and for removal
"nsa-frame-options-strategy",

View File

@ -0,0 +1,385 @@
/*
* Copyright 2002-2017 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.
*/
package org.springframework.security.config.method;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.jetbrains.annotations.NotNull;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationConfigurationException;
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.UsernamePasswordAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.method.configuration.MethodSecurityService;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
import org.springframework.security.test.context.support.WithAnonymousUser;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Josh Cummings
*/
@RunWith(SpringRunner.class)
@SecurityTestExecutionListeners
public class MethodSecurityBeanDefinitionParserTests {
private static final String CONFIG_LOCATION_PREFIX = "classpath:org/springframework/security/config/method/MethodSecurityBeanDefinitionParserTests";
private final UsernamePasswordAuthenticationToken bob = new UsernamePasswordAuthenticationToken("bob",
"bobspassword");
@Autowired(required = false)
MethodSecurityService methodSecurityService;
@Autowired(required = false)
BusinessService businessService;
@Rule
public final SpringTestRule spring = new SpringTestRule();
@WithMockUser(roles = "ADMIN")
@Test
public void preAuthorizeWhenRoleAdminThenAccessDeniedException() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorize)
.withMessage("Access Denied");
}
@WithAnonymousUser
@Test
public void preAuthorizePermitAllWhenRoleAnonymousThenPasses() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
String result = this.methodSecurityService.preAuthorizePermitAll();
assertThat(result).isNull();
}
@WithAnonymousUser
@Test
public void preAuthorizeNotAnonymousWhenRoleAnonymousThenAccessDeniedException() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(this.methodSecurityService::preAuthorizeNotAnonymous).withMessage("Access Denied");
}
@WithMockUser
@Test
public void preAuthorizeNotAnonymousWhenRoleUserThenPasses() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
this.methodSecurityService.preAuthorizeNotAnonymous();
}
@WithMockUser
@Test
public void securedWhenRoleUserThenAccessDeniedException() {
this.spring.configLocations(xml("MethodSecurityServiceEnabled")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured)
.withMessage("Access Denied");
}
@WithMockUser(roles = "ADMIN")
@Test
public void securedWhenRoleAdminThenPasses() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
String result = this.methodSecurityService.secured();
assertThat(result).isNull();
}
@WithMockUser(roles = "ADMIN")
@Test
public void securedUserWhenRoleAdminThenAccessDeniedException() {
this.spring.configLocations(xml("MethodSecurityServiceEnabled")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)
.withMessage("Access Denied");
}
@WithMockUser
@Test
public void securedUserWhenRoleUserThenPasses() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
String result = this.methodSecurityService.securedUser();
assertThat(result).isNull();
}
@WithMockUser
@Test
public void preAuthorizeAdminWhenRoleUserThenAccessDeniedException() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorizeAdmin)
.withMessage("Access Denied");
}
@WithMockUser(roles = "ADMIN")
@Test
public void preAuthorizeAdminWhenRoleAdminThenPasses() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
this.methodSecurityService.preAuthorizeAdmin();
}
@WithMockUser(authorities = "PREFIX_ADMIN")
@Test
public void preAuthorizeAdminWhenRoleAdminAndCustomPrefixThenPasses() {
this.spring.configLocations(xml("CustomGrantedAuthorityDefaults")).autowire();
this.methodSecurityService.preAuthorizeAdmin();
}
@WithMockUser
@Test
public void postHasPermissionWhenParameterIsNotGrantThenAccessDeniedException() {
this.spring.configLocations(xml("CustomPermissionEvaluator")).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.postHasPermission("deny")).withMessage("Access Denied");
}
@WithMockUser
@Test
public void postHasPermissionWhenParameterIsGrantThenPasses() {
this.spring.configLocations(xml("CustomPermissionEvaluator")).autowire();
String result = this.methodSecurityService.postHasPermission("grant");
assertThat(result).isNull();
}
@WithMockUser
@Test
public void postAnnotationWhenParameterIsNotGrantThenAccessDeniedException() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.postAnnotation("deny")).withMessage("Access Denied");
}
@WithMockUser
@Test
public void postAnnotationWhenParameterIsGrantThenPasses() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
String result = this.methodSecurityService.postAnnotation("grant");
assertThat(result).isNull();
}
@WithMockUser("bob")
@Test
public void methodReturningAListWhenPrePostFiltersConfiguredThenFiltersList() {
this.spring.configLocations(xml("BusinessService")).autowire();
List<String> names = new ArrayList<>();
names.add("bob");
names.add("joe");
names.add("sam");
List<?> result = this.businessService.methodReturningAList(names);
assertThat(result).hasSize(1);
assertThat(result.get(0)).isEqualTo("bob");
}
@WithMockUser("bob")
@Test
public void methodReturningAnArrayWhenPostFilterConfiguredThenFiltersArray() {
this.spring.configLocations(xml("BusinessService")).autowire();
List<String> names = new ArrayList<>();
names.add("bob");
names.add("joe");
names.add("sam");
Object[] result = this.businessService.methodReturningAnArray(names.toArray());
assertThat(result).hasSize(1);
assertThat(result[0]).isEqualTo("bob");
}
@WithMockUser("bob")
@Test
public void securedUserWhenCustomBeforeAdviceConfiguredAndNameBobThenPasses() {
this.spring.configLocations(xml("CustomAuthorizationManagerBeforeAdvice")).autowire();
String result = this.methodSecurityService.securedUser();
assertThat(result).isNull();
}
@WithMockUser("joe")
@Test
public void securedUserWhenCustomBeforeAdviceConfiguredAndNameNotBobThenAccessDeniedException() {
this.spring.configLocations(xml("CustomAuthorizationManagerBeforeAdvice")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)
.withMessage("Access Denied");
}
@WithMockUser("bob")
@Test
public void securedUserWhenCustomAfterAdviceConfiguredAndNameBobThenGranted() {
this.spring.configLocations(xml("CustomAuthorizationManagerAfterAdvice")).autowire();
String result = this.methodSecurityService.securedUser();
assertThat(result).isEqualTo("granted");
}
@WithMockUser("joe")
@Test
public void securedUserWhenCustomAfterAdviceConfiguredAndNameNotBobThenAccessDeniedException() {
this.spring.configLocations(xml("CustomAuthorizationManagerAfterAdvice")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::securedUser)
.withMessage("Access Denied for User 'joe'");
}
@WithMockUser(roles = "ADMIN")
@Test
public void jsr250WhenRoleAdminThenAccessDeniedException() {
this.spring.configLocations(xml("MethodSecurityServiceEnabled")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::jsr250)
.withMessage("Access Denied");
}
@WithAnonymousUser
@Test
public void jsr250PermitAllWhenRoleAnonymousThenPasses() {
this.spring.configLocations(xml("MethodSecurityServiceEnabled")).autowire();
String result = this.methodSecurityService.jsr250PermitAll();
assertThat(result).isNull();
}
@WithMockUser(roles = "ADMIN")
@Test
public void rolesAllowedUserWhenRoleAdminThenAccessDeniedException() {
this.spring.configLocations(xml("BusinessService")).autowire();
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.businessService::rolesAllowedUser)
.withMessage("Access Denied");
}
@WithMockUser
@Test
public void rolesAllowedUserWhenRoleUserThenPasses() {
this.spring.configLocations(xml("BusinessService")).autowire();
this.businessService.rolesAllowedUser();
}
@WithMockUser(roles = { "ADMIN", "USER" })
@Test
public void manyAnnotationsWhenMeetsConditionsThenReturnsFilteredList() throws Exception {
List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo");
this.spring.configLocations(xml("MethodSecurityServiceEnabled")).autowire();
List<String> filtered = this.methodSecurityService.manyAnnotations(new ArrayList<>(names));
assertThat(filtered).hasSize(2);
assertThat(filtered).containsExactly("harold", "jonathan");
}
// gh-4003
// gh-4103
@WithMockUser
@Test
public void manyAnnotationsWhenUserThenFails() {
List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo");
this.spring.configLocations(xml("MethodSecurityServiceEnabled")).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));
}
@WithMockUser
@Test
public void manyAnnotationsWhenShortListThenFails() {
List<String> names = Arrays.asList("harold", "jonathan", "pete");
this.spring.configLocations(xml("MethodSecurityServiceEnabled")).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));
}
@WithMockUser(roles = "ADMIN")
@Test
public void manyAnnotationsWhenAdminThenFails() {
List<String> names = Arrays.asList("harold", "jonathan", "pete", "bo");
this.spring.configLocations(xml("MethodSecurityServiceEnabled")).autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.manyAnnotations(new ArrayList<>(names)));
}
// gh-3183
@Test
public void repeatedAnnotationsWhenPresentThenFails() {
this.spring.configLocations(xml("MethodSecurityService")).autowire();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> this.methodSecurityService.repeatedAnnotations());
}
// gh-3183
@Test
public void repeatedJsr250AnnotationsWhenPresentThenFails() {
this.spring.configLocations(xml("Jsr250")).autowire();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> this.businessService.repeatedAnnotations());
}
// gh-3183
@Test
public void repeatedSecuredAnnotationsWhenPresentThenFails() {
this.spring.configLocations(xml("Secured")).autowire();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> this.businessService.repeatedAnnotations());
}
private static String xml(String configName) {
return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml";
}
static class MyPermissionEvaluator implements PermissionEvaluator {
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
return "grant".equals(targetDomainObject);
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,
Object permission) {
throw new UnsupportedOperationException();
}
}
static class MyAuthorizationManager implements AuthorizationManager<MethodInvocation> {
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation object) {
return new AuthorizationDecision("bob".equals(authentication.get().getName()));
}
}
static class MyAdvice implements MethodInterceptor {
@Nullable
@Override
public Object invoke(@NotNull MethodInvocation invocation) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if ("bob".equals(auth.getName())) {
return "granted";
}
throw new AccessDeniedException("Access Denied for User '" + auth.getName() + "'");
}
}
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2021 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 jsr250-enabled="true"/>
<b:bean class="org.springframework.security.access.annotation.ExpressionProtectedBusinessServiceImpl"/>
</b:beans>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2021 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:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<method-security/>
<b:bean id="pattern" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<b:property name="pattern" value=".*MethodSecurityServiceImpl.*securedUser"/>
</b:bean>
<b:bean id="rule" class="org.springframework.security.config.method.MethodSecurityBeanDefinitionParserTests.MyAdvice"/>
<aop:config>
<aop:advisor pointcut-ref="pattern" advice-ref="rule" order="#{T(org.springframework.security.authorization.method.AuthorizationInterceptorsOrder).POST_FILTER.getOrder() + 1}"/>
</aop:config>
<b:bean id="methodSecurityService" class="org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl"/>
</b:beans>

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2021 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:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<method-security/>
<aop:config/>
<b:bean id="myAdvice" class="org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor">
<b:constructor-arg>
<b:bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<b:property name="pattern" value=".*MethodSecurityServiceImpl.*securedUser"/>
</b:bean>
</b:constructor-arg>
<b:constructor-arg>
<b:bean class="org.springframework.security.config.method.MethodSecurityBeanDefinitionParserTests.MyAuthorizationManager"/>
</b:constructor-arg>
</b:bean>
<b:bean id="methodSecurityService" class="org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl"/>
</b:beans>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2021 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/>
<b:bean class="org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl"/>
<b:bean id="grantedAuthorityDefaults" class="org.springframework.security.config.core.GrantedAuthorityDefaults">
<b:constructor-arg value="PREFIX_"/>
</b:bean>
</b:beans>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2021 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>
<expression-handler ref="expressionHandler"/>
</method-security>
<b:bean class="org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl"/>
<b:bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<b:property name="permissionEvaluator">
<b:bean class="org.springframework.security.config.method.MethodSecurityBeanDefinitionParserTests.MyPermissionEvaluator"/>
</b:property>
</b:bean>
</b:beans>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2021 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 pre-post-enabled="false" jsr250-enabled="true"/>
<b:bean class="org.springframework.security.access.annotation.Jsr250BusinessServiceImpl"/>
</b:beans>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2021 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/>
<b:bean class="org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl"/>
</b:beans>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2021 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"/>
<b:bean class="org.springframework.security.config.annotation.method.configuration.MethodSecurityServiceImpl"/>
</b:beans>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2002-2021 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 pre-post-enabled="false" secured-enabled="true"/>
<b:bean class="org.springframework.security.access.annotation.BusinessServiceImpl"/>
</b:beans>

View File

@ -1,123 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.lang.NonNull;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.authorization.AuthorityAuthorizationManager;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has
* access to the {@link MethodInvocation} by evaluating if the {@link Authentication}
* contains a specified authority from the JSR-250 security annotations.
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class Jsr250AuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> {
private static final Set<Class<? extends Annotation>> JSR250_ANNOTATIONS = new HashSet<>();
static {
JSR250_ANNOTATIONS.add(DenyAll.class);
JSR250_ANNOTATIONS.add(PermitAll.class);
JSR250_ANNOTATIONS.add(RolesAllowed.class);
}
private final Jsr250AuthorizationManagerRegistry registry = new Jsr250AuthorizationManagerRegistry();
private String rolePrefix = "ROLE_";
/**
* Sets the role prefix. Defaults to "ROLE_".
* @param rolePrefix the role prefix to use
*/
public void setRolePrefix(String rolePrefix) {
Assert.notNull(rolePrefix, "rolePrefix cannot be null");
this.rolePrefix = rolePrefix;
}
/**
* Determines if an {@link Authentication} has access to the {@link MethodInvocation}
* by evaluating if the {@link Authentication} contains a specified authority from the
* JSR-250 security annotations.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @return an {@link AuthorizationDecision} or null if the JSR-250 security
* annotations is not present
*/
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext) {
AuthorizationManager<MethodAuthorizationContext> delegate = this.registry
.getManager(methodAuthorizationContext);
return delegate.check(authentication, methodAuthorizationContext);
}
private final class Jsr250AuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry {
@NonNull
@Override
AuthorizationManager<MethodAuthorizationContext> resolveManager(Method method, Class<?> targetClass) {
for (Annotation annotation : findJsr250Annotations(method, targetClass)) {
if (annotation instanceof DenyAll) {
return (a, o) -> new AuthorizationDecision(false);
}
if (annotation instanceof PermitAll) {
return (a, o) -> new AuthorizationDecision(true);
}
if (annotation instanceof RolesAllowed) {
RolesAllowed rolesAllowed = (RolesAllowed) annotation;
return AuthorityAuthorizationManager.hasAnyRole(Jsr250AuthorizationManager.this.rolePrefix,
rolesAllowed.value());
}
}
return NULL_MANAGER;
}
private Set<Annotation> findJsr250Annotations(Method method, Class<?> targetClass) {
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
Set<Annotation> annotations = findAnnotations(specificMethod);
return (annotations.isEmpty()) ? findAnnotations(specificMethod.getDeclaringClass()) : annotations;
}
private Set<Annotation> findAnnotations(AnnotatedElement annotatedElement) {
return AnnotatedElementUtils.findAllMergedAnnotations(annotatedElement, JSR250_ANNOTATIONS);
}
}
}

View File

@ -1,82 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.intercept.aopalliance;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.lang.NonNull;
import org.springframework.security.access.method.AuthorizationMethodAfterAdvice;
import org.springframework.security.access.method.AuthorizationMethodBeforeAdvice;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* Provides security interception of AOP Alliance based method invocations.
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class AuthorizationMethodInterceptor implements MethodInterceptor {
private final AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> beforeAdvice;
private final AuthorizationMethodAfterAdvice<MethodAuthorizationContext> afterAdvice;
/**
* Creates an instance.
* @param beforeAdvice the {@link AuthorizationMethodBeforeAdvice} to use
* @param afterAdvice the {@link AuthorizationMethodAfterAdvice} to use
*/
public AuthorizationMethodInterceptor(AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> beforeAdvice,
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> afterAdvice) {
this.beforeAdvice = beforeAdvice;
this.afterAdvice = afterAdvice;
}
/**
* This method should be used to enforce security on a {@link MethodInvocation}.
* @param mi the method being invoked which requires a security decision
* @return the returned value from the {@link MethodInvocation}
*/
@Override
public Object invoke(@NonNull MethodInvocation mi) throws Throwable {
MethodAuthorizationContext methodAuthorizationContext = getMethodAuthorizationContext(mi);
this.beforeAdvice.before(this::getAuthentication, methodAuthorizationContext);
Object returnedObject = mi.proceed();
return this.afterAdvice.after(this::getAuthentication, methodAuthorizationContext, returnedObject);
}
private MethodAuthorizationContext getMethodAuthorizationContext(MethodInvocation mi) {
Object target = mi.getThis();
Class<?> targetClass = (target != null) ? AopUtils.getTargetClass(target) : null;
return new MethodAuthorizationContext(mi, targetClass);
}
private Authentication getAuthentication() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new AuthenticationCredentialsNotFoundException(
"An Authentication object was not found in the SecurityContext");
}
return authentication;
}
}

View File

@ -1,73 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.method;
import java.util.function.Supplier;
import org.springframework.aop.MethodMatcher;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationMethodAfterAdvice} which can determine if an
* {@link Authentication} has access to the {@link T} object using an
* {@link AuthorizationManager} if a {@link MethodMatcher} matches.
*
* @param <T> the type of object that the authorization check is being done one.
* @author Evgeniy Cheban
* @since 5.5
*/
public final class AuthorizationManagerMethodAfterAdvice<T> implements AuthorizationMethodAfterAdvice<T> {
private final MethodMatcher methodMatcher;
private final AuthorizationManager<T> authorizationManager;
/**
* Creates an instance.
* @param methodMatcher the {@link MethodMatcher} to use
* @param authorizationManager the {@link AuthorizationManager} to use
*/
public AuthorizationManagerMethodAfterAdvice(MethodMatcher methodMatcher,
AuthorizationManager<T> authorizationManager) {
Assert.notNull(methodMatcher, "methodMatcher cannot be null");
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
this.methodMatcher = methodMatcher;
this.authorizationManager = authorizationManager;
}
/**
* Determines if an {@link Authentication} has access to the {@link T} object using
* the {@link AuthorizationManager}.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
* @throws AccessDeniedException if access is not granted
*/
@Override
public Object after(Supplier<Authentication> authentication, T object, Object returnedObject) {
this.authorizationManager.verify(authentication, object);
return returnedObject;
}
@Override
public MethodMatcher getMethodMatcher() {
return this.methodMatcher;
}
}

View File

@ -1,72 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.method;
import java.util.function.Supplier;
import org.springframework.aop.MethodMatcher;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationMethodBeforeAdvice} which can determine if an
* {@link Authentication} has access to the {@link T} object using an
* {@link AuthorizationManager} if a {@link MethodMatcher} matches.
*
* @param <T> the type of object that the authorization check is being done one.
* @author Evgeniy Cheban
* @since 5.5
*/
public final class AuthorizationManagerMethodBeforeAdvice<T> implements AuthorizationMethodBeforeAdvice<T> {
private final MethodMatcher methodMatcher;
private final AuthorizationManager<T> authorizationManager;
/**
* Creates an instance.
* @param methodMatcher the {@link MethodMatcher} to use
* @param authorizationManager the {@link AuthorizationManager} to use
*/
public AuthorizationManagerMethodBeforeAdvice(MethodMatcher methodMatcher,
AuthorizationManager<T> authorizationManager) {
Assert.notNull(methodMatcher, "methodMatcher cannot be null");
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
this.methodMatcher = methodMatcher;
this.authorizationManager = authorizationManager;
}
/**
* Determines if an {@link Authentication} has access to the {@link T} object using
* the {@link AuthorizationManager}.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
* @throws AccessDeniedException if access is not granted
*/
@Override
public void before(Supplier<Authentication> authentication, T object) {
this.authorizationManager.verify(authentication, object);
}
@Override
public MethodMatcher getMethodMatcher() {
return this.methodMatcher;
}
}

View File

@ -1,61 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.method;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.Pointcut;
import org.springframework.security.core.Authentication;
/**
* An Authorization advice that can determine if an {@link Authentication} has access to
* the returned object from the {@link MethodInvocation}. The {@link #getMethodMatcher()}
* describes when the advice applies for the method.
*
* @param <T> the type of object that the authorization check is being done one.
* @author Evgeniy Cheban
* @since 5.5
*/
public interface AuthorizationMethodAfterAdvice<T> extends Pointcut {
/**
* Returns the default {@link ClassFilter}.
* @return the {@link ClassFilter#TRUE} to use
*/
@Override
default ClassFilter getClassFilter() {
return ClassFilter.TRUE;
}
/**
* Determines if an {@link Authentication} has access to the returned object from the
* {@link MethodInvocation}.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
* @param returnedObject the returned object from the {@link MethodInvocation} to
* check
* @return the <code>Object</code> that will ultimately be returned to the caller (if
* an implementation does not wish to modify the object to be returned to the caller,
* the implementation should simply return the same object it was passed by the
* <code>returnedObject</code> method argument)
*/
Object after(Supplier<Authentication> authentication, T object, Object returnedObject);
}

View File

@ -1,52 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.method;
import java.util.function.Supplier;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.Pointcut;
import org.springframework.security.core.Authentication;
/**
* An advice which can determine if an {@link Authentication} has access to the {@link T}
* object. The {@link #getMethodMatcher()} describes when the advice applies for the
* method.
*
* @param <T> the type of object that the authorization check is being done one.
* @author Evgeniy Cheban
* @since 5.5
*/
public interface AuthorizationMethodBeforeAdvice<T> extends Pointcut {
/**
* Returns the default {@link ClassFilter}.
* @return the {@link ClassFilter#TRUE} to use
*/
@Override
default ClassFilter getClassFilter() {
return ClassFilter.TRUE;
}
/**
* Determines if an {@link Authentication} has access to the {@link T} object.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param object the {@link T} object to check
*/
void before(Supplier<Authentication> authentication, T object);
}

View File

@ -1,102 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.method;
import java.lang.reflect.Method;
import java.util.List;
import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.support.StaticMethodMatcher;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.Authentication;
/**
* An {@link AuthorizationMethodAfterAdvice} which delegates to specific
* {@link AuthorizationMethodAfterAdvice}s and returns the result (possibly modified) from
* the {@link MethodInvocation}.
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class DelegatingAuthorizationMethodAfterAdvice
implements AuthorizationMethodAfterAdvice<MethodAuthorizationContext> {
private final Log logger = LogFactory.getLog(getClass());
private final MethodMatcher methodMatcher = new StaticMethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
for (AuthorizationMethodAfterAdvice<MethodAuthorizationContext> delegate : DelegatingAuthorizationMethodAfterAdvice.this.delegates) {
MethodMatcher methodMatcher = delegate.getMethodMatcher();
if (methodMatcher.matches(method, targetClass)) {
return true;
}
}
return false;
}
};
private final List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> delegates;
/**
* Creates an instance.
* @param delegates the {@link AuthorizationMethodAfterAdvice}s to use
*/
public DelegatingAuthorizationMethodAfterAdvice(
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> delegates) {
this.delegates = delegates;
}
@Override
public MethodMatcher getMethodMatcher() {
return this.methodMatcher;
}
/**
* Delegates to specific {@link AuthorizationMethodAfterAdvice}s and returns the
* <code>returnedObject</code> (possibly modified) from the method argument.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @param returnedObject the returned object from the {@link MethodInvocation} to
* check
* @return the <code>returnedObject</code> (possibly modified) from the method
* argument
*/
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext methodAuthorizationContext,
Object returnedObject) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(
LogMessage.format("Post Authorizing %s from %s", returnedObject, methodAuthorizationContext));
}
Object result = returnedObject;
for (AuthorizationMethodAfterAdvice<MethodAuthorizationContext> delegate : this.delegates) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Checking authorization on %s from %s using %s", result,
methodAuthorizationContext, delegate));
}
result = delegate.after(authentication, methodAuthorizationContext, result);
}
return result;
}
}

View File

@ -1,95 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.method;
import java.lang.reflect.Method;
import java.util.List;
import java.util.function.Supplier;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.support.StaticMethodMatcher;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.Authentication;
/**
* An {@link AuthorizationMethodBeforeAdvice} which delegates to a specific
* {@link AuthorizationMethodBeforeAdvice} and grants access if all
* {@link AuthorizationMethodBeforeAdvice}s granted or abstained. Denies access only if
* one of the {@link AuthorizationMethodBeforeAdvice}s denied.
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class DelegatingAuthorizationMethodBeforeAdvice
implements AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> {
private final Log logger = LogFactory.getLog(getClass());
private final MethodMatcher methodMatcher = new StaticMethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
for (AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> delegate : DelegatingAuthorizationMethodBeforeAdvice.this.delegates) {
MethodMatcher methodMatcher = delegate.getMethodMatcher();
if (methodMatcher.matches(method, targetClass)) {
return true;
}
}
return false;
}
};
private final List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> delegates;
/**
* Creates an instance.
* @param delegates the {@link AuthorizationMethodBeforeAdvice}s to use
*/
public DelegatingAuthorizationMethodBeforeAdvice(
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> delegates) {
this.delegates = delegates;
}
@Override
public MethodMatcher getMethodMatcher() {
return this.methodMatcher;
}
/**
* Delegates to a specific {@link AuthorizationMethodBeforeAdvice} and grants access
* if all {@link AuthorizationMethodBeforeAdvice}s granted or abstained. Denies only
* if one of the {@link AuthorizationMethodBeforeAdvice}s denied.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
*/
@Override
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext methodAuthorizationContext) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Pre Authorizing %s", methodAuthorizationContext));
}
for (AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> delegate : this.delegates) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Checking authorization on %s using %s", methodAuthorizationContext,
delegate));
}
delegate.before(authentication, methodAuthorizationContext);
}
}
}

View File

@ -1,84 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.method;
import org.aopalliance.intercept.MethodInvocation;
/**
* An authorization context which is holds the {@link MethodInvocation}, the target class
* and the returned object.
*
* @author Evgeniy Cheban
* @since 5.5
*/
public final class MethodAuthorizationContext {
private final MethodInvocation methodInvocation;
private final Class<?> targetClass;
private Object returnObject;
/**
* Creates an instance.
* @param methodInvocation the {@link MethodInvocation} to use
* @param targetClass the target class to use
*/
public MethodAuthorizationContext(MethodInvocation methodInvocation, Class<?> targetClass) {
this.methodInvocation = methodInvocation;
this.targetClass = targetClass;
}
/**
* Returns the {@link MethodInvocation}.
* @return the {@link MethodInvocation} to use
*/
public MethodInvocation getMethodInvocation() {
return this.methodInvocation;
}
/**
* Returns the target class.
* @return the target class to use
*/
public Class<?> getTargetClass() {
return this.targetClass;
}
/**
* Returns the returned object from the {@link MethodInvocation}.
* @return the returned object from the {@link MethodInvocation} to use
*/
public Object getReturnObject() {
return this.returnObject;
}
/**
* Sets the returned object from the {@link MethodInvocation}.
* @param returnObject the returned object from the {@link MethodInvocation} to use
*/
public void setReturnObject(Object returnObject) {
this.returnObject = returnObject;
}
@Override
public String toString() {
return "MethodAuthorizationContext[methodInvocation=" + this.methodInvocation + ", targetClass="
+ this.targetClass + ", returnObject=" + this.returnObject + ']';
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.security.access.annotation;
package org.springframework.security.authorization.method;
import java.lang.reflect.Method;
import java.util.Map;
@ -22,34 +22,31 @@ import java.util.concurrent.ConcurrentHashMap;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.MethodClassKey;
import org.springframework.lang.NonNull;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.authorization.AuthorizationManager;
/**
* An abstract registry which provides an {@link AuthorizationManager} for the
* {@link MethodInvocation}.
* For internal use only, as this contract is likely to change
*
* @author Evgeniy Cheban
* @since 5.5
*/
abstract class AbstractAuthorizationManagerRegistry {
static final AuthorizationManager<MethodAuthorizationContext> NULL_MANAGER = (a, o) -> null;
static final AuthorizationManager<MethodInvocation> NULL_MANAGER = (a, o) -> null;
private final Map<MethodClassKey, AuthorizationManager<MethodAuthorizationContext>> cachedManagers = new ConcurrentHashMap<>();
private final Map<MethodClassKey, AuthorizationManager<MethodInvocation>> cachedManagers = new ConcurrentHashMap<>();
/**
* Returns an {@link AuthorizationManager} for the {@link MethodAuthorizationContext}.
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to use
* Returns an {@link AuthorizationManager} for the {@link MethodInvocation}.
* @param methodInvocation the {@link MethodInvocation} to use
* @return an {@link AuthorizationManager} to use
*/
final AuthorizationManager<MethodAuthorizationContext> getManager(
MethodAuthorizationContext methodAuthorizationContext) {
MethodInvocation methodInvocation = methodAuthorizationContext.getMethodInvocation();
final AuthorizationManager<MethodInvocation> getManager(MethodInvocation methodInvocation) {
Method method = methodInvocation.getMethod();
Class<?> targetClass = methodAuthorizationContext.getTargetClass();
Object target = methodInvocation.getThis();
Class<?> targetClass = (target != null) ? AopUtils.getTargetClass(target) : null;
MethodClassKey cacheKey = new MethodClassKey(method, targetClass);
return this.cachedManagers.computeIfAbsent(cacheKey, (k) -> resolveManager(method, targetClass));
}
@ -62,6 +59,6 @@ abstract class AbstractAuthorizationManagerRegistry {
* @return the non-null {@link AuthorizationManager}
*/
@NonNull
abstract AuthorizationManager<MethodAuthorizationContext> resolveManager(Method method, Class<?> targetClass);
abstract AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass);
}

View File

@ -22,30 +22,28 @@ import java.util.concurrent.ConcurrentHashMap;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.MethodClassKey;
import org.springframework.lang.NonNull;
import org.springframework.security.access.method.MethodAuthorizationContext;
/**
* An abstract registry which provides an {@link ExpressionAttribute} for the
* {@link MethodInvocation}.
* For internal use only, as this contract is likely to change
*
* @author Evgeniy Cheban
* @since 5.5
*/
abstract class AbstractExpressionAttributeRegistry<T extends ExpressionAttribute> {
private final Map<MethodClassKey, T> cachedAttributes = new ConcurrentHashMap<>();
/**
* Returns an {@link ExpressionAttribute} for the {@link MethodAuthorizationContext}.
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to use
* Returns an {@link ExpressionAttribute} for the {@link MethodInvocation}.
* @param mi the {@link MethodInvocation} to use
* @return the {@link ExpressionAttribute} to use
*/
final T getAttribute(MethodAuthorizationContext methodAuthorizationContext) {
MethodInvocation methodInvocation = methodAuthorizationContext.getMethodInvocation();
Method method = methodInvocation.getMethod();
Class<?> targetClass = methodAuthorizationContext.getTargetClass();
final T getAttribute(MethodInvocation mi) {
Method method = mi.getMethod();
Object target = mi.getThis();
Class<?> targetClass = (target != null) ? AopUtils.getTargetClass(target) : null;
return getAttribute(method, targetClass);
}

View File

@ -0,0 +1,113 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.authorization.method;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.annotation.RepeatableContainers;
/**
* A wrapper around {@link AnnotationUtils} that checks for, and errors on, conflicting
* annotations. This is specifically important for Spring Security annotations which are
* not designed to be repeatable.
*
* There are numerous ways that two annotations of the same type may be attached to the
* same method. For example, a class may implement a method defined in two separate
* interfaces. If both of those interfaces have a `@PreAuthorize` annotation, then it's
* unclear which `@PreAuthorize` expression Spring Security should use.
*
* Another way is when one of Spring Security's annotations is used as a meta-annotation.
* In that case, two custom annotations can be declared, each with their own
* `@PreAuthorize` declaration. If both custom annotations are used on the same method,
* then it's unclear which `@PreAuthorize` expression Spring Security should use.
*
* @author Josh Cummings
*/
final class AuthorizationAnnotationUtils {
/**
* Perform an exhaustive search on the type hierarchy of the given {@link Method} for
* the annotation of type {@code annotationType}, including any annotations using
* {@code annotationType} as a meta-annotation.
*
* If more than one is found, then throw an error.
* @param method the method declaration to search from
* @param annotationType the annotation type to search for
* @return the unique instance of the annotation attributed to the method,
* {@code null} otherwise
* @throws AnnotationConfigurationException if more than one instance of the
* annotation is found
*/
static <A extends Annotation> A findUniqueAnnotation(Method method, Class<A> annotationType) {
MergedAnnotations mergedAnnotations = MergedAnnotations.from(method,
MergedAnnotations.SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none());
if (hasDuplicate(mergedAnnotations, annotationType)) {
throw new AnnotationConfigurationException("Found more than one annotation of type " + annotationType
+ " attributed to " + method
+ " Please remove the duplicate annotations and publish a bean to handle your authorization logic.");
}
return AnnotationUtils.findAnnotation(method, annotationType);
}
/**
* Perform an exhaustive search on the type hierarchy of the given {@link Class} for
* the annotation of type {@code annotationType}, including any annotations using
* {@code annotationType} as a meta-annotation.
*
* If more than one is found, then throw an error.
* @param type the type to search from
* @param annotationType the annotation type to search for
* @return the unique instance of the annotation attributed to the method,
* {@code null} otherwise
* @throws AnnotationConfigurationException if more than one instance of the
* annotation is found
*/
static <A extends Annotation> A findUniqueAnnotation(Class<?> type, Class<A> annotationType) {
MergedAnnotations mergedAnnotations = MergedAnnotations.from(type,
MergedAnnotations.SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none());
if (hasDuplicate(mergedAnnotations, annotationType)) {
throw new AnnotationConfigurationException("Found more than one annotation of type " + annotationType
+ " attributed to " + type
+ " Please remove the duplicate annotations and publish a bean to handle your authorization logic.");
}
return AnnotationUtils.findAnnotation(type, annotationType);
}
private static <A extends Annotation> boolean hasDuplicate(MergedAnnotations mergedAnnotations,
Class<A> annotationType) {
boolean alreadyFound = false;
for (MergedAnnotation<Annotation> mergedAnnotation : mergedAnnotations) {
if (mergedAnnotation.getType() == annotationType) {
if (alreadyFound) {
return true;
}
alreadyFound = true;
}
}
return false;
}
private AuthorizationAnnotationUtils() {
}
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.authorization.method;
import org.springframework.aop.Advisor;
/**
* Ordering of Spring Security's authorization {@link Advisor}s
*
* @author Josh Cummings
* @since 5.6
* @see PreAuthorizeAuthorizationManager
* @see PostAuthorizeAuthorizationManager
* @see SecuredAuthorizationManager
* @see Jsr250AuthorizationManager
*/
public enum AuthorizationInterceptorsOrder {
FIRST(Integer.MIN_VALUE),
/**
* {@link PreFilterAuthorizationMethodInterceptor}
*/
PRE_FILTER,
PRE_AUTHORIZE,
SECURED,
JSR250,
POST_AUTHORIZE,
/**
* {@link PostFilterAuthorizationMethodInterceptor}
*/
POST_FILTER,
LAST(Integer.MAX_VALUE);
private static final int INTERVAL = 100;
private final int order;
AuthorizationInterceptorsOrder() {
this.order = ordinal() * INTERVAL;
}
AuthorizationInterceptorsOrder(int order) {
this.order = order;
}
public int getOrder() {
return this.order;
}
}

View File

@ -0,0 +1,137 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.authorization.method;
import java.util.function.Supplier;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.core.Ordered;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
/**
* A {@link MethodInterceptor} which can determine if an {@link Authentication} has access
* to the result of an {@link MethodInvocation} using an {@link AuthorizationManager}
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.6
*/
public final class AuthorizationManagerAfterMethodInterceptor
implements Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
static final Supplier<Authentication> AUTHENTICATION_SUPPLIER = () -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new AuthenticationCredentialsNotFoundException(
"An Authentication object was not found in the SecurityContext");
}
return authentication;
};
private final Pointcut pointcut;
private final AuthorizationManager<MethodInvocationResult> authorizationManager;
private int order;
/**
* Creates an instance.
* @param pointcut the {@link Pointcut} to use
* @param authorizationManager the {@link AuthorizationManager} to use
*/
public AuthorizationManagerAfterMethodInterceptor(Pointcut pointcut,
AuthorizationManager<MethodInvocationResult> authorizationManager) {
Assert.notNull(pointcut, "pointcut cannot be null");
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
this.pointcut = pointcut;
this.authorizationManager = authorizationManager;
}
/**
* Creates an interceptor for the {@link PostAuthorize} annotation
* @return the interceptor
*/
public static AuthorizationManagerAfterMethodInterceptor postAuthorize() {
return postAuthorize(new PostAuthorizeAuthorizationManager());
}
/**
* Creates an interceptor for the {@link PostAuthorize} annotation
* @param authorizationManager the {@link PostAuthorizeAuthorizationManager} to use
* @return the interceptor
*/
public static AuthorizationManagerAfterMethodInterceptor postAuthorize(
PostAuthorizeAuthorizationManager authorizationManager) {
AuthorizationManagerAfterMethodInterceptor interceptor = new AuthorizationManagerAfterMethodInterceptor(
AuthorizationMethodPointcuts.forAnnotations(PostAuthorize.class), authorizationManager);
interceptor.setOrder(500);
return interceptor;
}
/**
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
* using the {@link AuthorizationManager}.
* @param mi the {@link MethodInvocation} to check
* @throws AccessDeniedException if access is not granted
*/
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Object result = mi.proceed();
this.authorizationManager.verify(AUTHENTICATION_SUPPLIER, new MethodInvocationResult(mi, result));
return result;
}
@Override
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
/**
* {@inheritDoc}
*/
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public Advice getAdvice() {
return this;
}
@Override
public boolean isPerInstance() {
return true;
}
}

View File

@ -0,0 +1,183 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.authorization.method;
import java.util.function.Supplier;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.core.Ordered;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
/**
* A {@link MethodInterceptor} which uses a {@link AuthorizationManager} to determine if
* an {@link Authentication} may invoke the given {@link MethodInvocation}
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.6
*/
public final class AuthorizationManagerBeforeMethodInterceptor
implements Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
static final Supplier<Authentication> AUTHENTICATION_SUPPLIER = () -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new AuthenticationCredentialsNotFoundException(
"An Authentication object was not found in the SecurityContext");
}
return authentication;
};
private final Pointcut pointcut;
private final AuthorizationManager<MethodInvocation> authorizationManager;
private int order = AuthorizationInterceptorsOrder.FIRST.getOrder();
/**
* Creates an instance.
* @param pointcut the {@link Pointcut} to use
* @param authorizationManager the {@link AuthorizationManager} to use
*/
public AuthorizationManagerBeforeMethodInterceptor(Pointcut pointcut,
AuthorizationManager<MethodInvocation> authorizationManager) {
Assert.notNull(pointcut, "pointcut cannot be null");
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
this.pointcut = pointcut;
this.authorizationManager = authorizationManager;
}
/**
* Creates an interceptor for the {@link PreAuthorize} annotation
* @return the interceptor
*/
public static AuthorizationManagerBeforeMethodInterceptor preAuthorize() {
return preAuthorize(new PreAuthorizeAuthorizationManager());
}
/**
* Creates an interceptor for the {@link PreAuthorize} annotation
* @param authorizationManager the {@link PreAuthorizeAuthorizationManager} to use
* @return the interceptor
*/
public static AuthorizationManagerBeforeMethodInterceptor preAuthorize(
PreAuthorizeAuthorizationManager authorizationManager) {
AuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(
AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class), authorizationManager);
interceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE.getOrder());
return interceptor;
}
/**
* Creates an interceptor for the {@link Secured} annotation
* @return the interceptor
*/
public static AuthorizationManagerBeforeMethodInterceptor secured() {
return secured(new SecuredAuthorizationManager());
}
/**
* Creates an interceptor for the {@link Secured} annotation
* @param authorizationManager the {@link SecuredAuthorizationManager} to use
* @return the interceptor
*/
public static AuthorizationManagerBeforeMethodInterceptor secured(
SecuredAuthorizationManager authorizationManager) {
AuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(
AuthorizationMethodPointcuts.forAnnotations(Secured.class), authorizationManager);
interceptor.setOrder(AuthorizationInterceptorsOrder.SECURED.getOrder());
return interceptor;
}
/**
* Creates an interceptor for the JSR-250 annotations
* @return the interceptor
*/
public static AuthorizationManagerBeforeMethodInterceptor jsr250() {
return jsr250(new Jsr250AuthorizationManager());
}
/**
* Creates an interceptor for the JSR-250 annotations
* @param authorizationManager the {@link Jsr250AuthorizationManager} to use
* @return the interceptor
*/
public static AuthorizationManagerBeforeMethodInterceptor jsr250(Jsr250AuthorizationManager authorizationManager) {
AuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(
AuthorizationMethodPointcuts.forAnnotations(RolesAllowed.class, DenyAll.class, PermitAll.class),
authorizationManager);
interceptor.setOrder(AuthorizationInterceptorsOrder.JSR250.getOrder());
return interceptor;
}
/**
* Determine if an {@link Authentication} has access to the {@link MethodInvocation}
* using the configured {@link AuthorizationManager}.
* @param mi the {@link MethodInvocation} to check
* @throws AccessDeniedException if access is not granted
*/
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
this.authorizationManager.verify(AUTHENTICATION_SUPPLIER, mi);
return mi.proceed();
}
@Override
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
/**
* {@inheritDoc}
*/
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public Advice getAdvice() {
return this;
}
@Override
public boolean isPerInstance() {
return true;
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.authorization.method;
import java.lang.annotation.Annotation;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.Pointcuts;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
/**
* @author Josh Cummings
*/
final class AuthorizationMethodPointcuts {
@SafeVarargs
static Pointcut forAnnotations(Class<? extends Annotation>... annotations) {
ComposablePointcut pointcut = null;
for (Class<? extends Annotation> annotation : annotations) {
if (pointcut == null) {
pointcut = new ComposablePointcut(classOrMethod(annotation));
}
else {
pointcut.union(classOrMethod(annotation));
}
}
return pointcut;
}
private static Pointcut classOrMethod(Class<? extends Annotation> annotation) {
return Pointcuts.union(new AnnotationMatchingPointcut(null, annotation, true),
new AnnotationMatchingPointcut(annotation, true));
}
private AuthorizationMethodPointcuts() {
}
}

View File

@ -0,0 +1,151 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.authorization.method;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.lang.NonNull;
import org.springframework.security.authorization.AuthorityAuthorizationManager;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* invoke the {@link MethodInvocation} by evaluating if the {@link Authentication}
* contains a specified authority from the JSR-250 security annotations.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.6
*/
public final class Jsr250AuthorizationManager implements AuthorizationManager<MethodInvocation> {
private static final Set<Class<? extends Annotation>> JSR250_ANNOTATIONS = new HashSet<>();
static {
JSR250_ANNOTATIONS.add(DenyAll.class);
JSR250_ANNOTATIONS.add(PermitAll.class);
JSR250_ANNOTATIONS.add(RolesAllowed.class);
}
private final Jsr250AuthorizationManagerRegistry registry = new Jsr250AuthorizationManagerRegistry();
private String rolePrefix = "ROLE_";
/**
* Sets the role prefix. Defaults to "ROLE_".
* @param rolePrefix the role prefix to use
*/
public void setRolePrefix(String rolePrefix) {
Assert.notNull(rolePrefix, "rolePrefix cannot be null");
this.rolePrefix = rolePrefix;
}
/**
* Determine if an {@link Authentication} has access to a method by evaluating the
* {@link DenyAll}, {@link PermitAll}, and {@link RolesAllowed} annotations that
* {@link MethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodInvocation the {@link MethodInvocation} to check
* @return an {@link AuthorizationDecision} or null if the JSR-250 security
* annotations is not present
*/
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation methodInvocation) {
AuthorizationManager<MethodInvocation> delegate = this.registry.getManager(methodInvocation);
return delegate.check(authentication, methodInvocation);
}
private final class Jsr250AuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry {
@NonNull
@Override
AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass) {
Annotation annotation = findJsr250Annotation(method, targetClass);
if (annotation instanceof DenyAll) {
return (a, o) -> new AuthorizationDecision(false);
}
if (annotation instanceof PermitAll) {
return (a, o) -> new AuthorizationDecision(true);
}
if (annotation instanceof RolesAllowed) {
RolesAllowed rolesAllowed = (RolesAllowed) annotation;
return AuthorityAuthorizationManager.hasAnyRole(Jsr250AuthorizationManager.this.rolePrefix,
rolesAllowed.value());
}
return NULL_MANAGER;
}
private Annotation findJsr250Annotation(Method method, Class<?> targetClass) {
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
Annotation annotation = findAnnotation(specificMethod);
return (annotation != null) ? annotation : findAnnotation(specificMethod.getDeclaringClass());
}
private Annotation findAnnotation(Method method) {
Set<Annotation> annotations = new HashSet<>();
for (Class<? extends Annotation> annotationClass : JSR250_ANNOTATIONS) {
Annotation annotation = AuthorizationAnnotationUtils.findUniqueAnnotation(method, annotationClass);
if (annotation != null) {
annotations.add(annotation);
}
}
if (annotations.isEmpty()) {
return null;
}
if (annotations.size() > 1) {
throw new AnnotationConfigurationException(
"The JSR-250 specification disallows DenyAll, PermitAll, and RolesAllowed from appearing on the same method.");
}
return annotations.iterator().next();
}
private Annotation findAnnotation(Class<?> clazz) {
Set<Annotation> annotations = new HashSet<>();
for (Class<? extends Annotation> annotationClass : JSR250_ANNOTATIONS) {
Annotation annotation = AuthorizationAnnotationUtils.findUniqueAnnotation(clazz, annotationClass);
if (annotation != null) {
annotations.add(annotation);
}
}
if (annotations.isEmpty()) {
return null;
}
if (annotations.size() > 1) {
throw new AnnotationConfigurationException(
"The JSR-250 specification disallows DenyAll, PermitAll, and RolesAllowed from appearing on the same class definition.");
}
return annotations.iterator().next();
}
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.authorization.method;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.util.Assert;
/**
* A context object that contains a {@link MethodInvocation} and the result of that
* {@link MethodInvocation}.
*
* @author Josh Cummings
* @since 5.6
*/
public class MethodInvocationResult {
private final MethodInvocation methodInvocation;
private final Object result;
/**
* Construct a {@link MethodInvocationResult} with the provided parameters
* @param methodInvocation the already-invoked {@link MethodInvocation}
* @param result the value returned from the {@link MethodInvocation}
*/
public MethodInvocationResult(MethodInvocation methodInvocation, Object result) {
Assert.notNull(methodInvocation, "methodInvocation cannot be null");
this.methodInvocation = methodInvocation;
this.result = result;
}
/**
* Return the already-invoked {@link MethodInvocation}
* @return the already-invoked {@link MethodInvocation}
*/
public MethodInvocation getMethodInvocation() {
return this.methodInvocation;
}
/**
* Return the result of the already-invoked {@link MethodInvocation}
* @return the result
*/
public Object getResult() {
return this.result;
}
}

View File

@ -23,13 +23,11 @@ import org.aopalliance.intercept.MethodInvocation;
import reactor.util.annotation.NonNull;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.security.access.expression.ExpressionUtils;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
@ -37,21 +35,21 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has
* access to the {@link MethodInvocation} by evaluating an expression from the
* {@link PostAuthorize} annotation.
* An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* return the result from an invoked {@link MethodInvocation} by evaluating an expression
* from the {@link PostAuthorize} annotation.
*
* @author Evgeniy Cheban
* @since 5.5
* @since 5.6
*/
public final class PostAuthorizeAuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> {
public final class PostAuthorizeAuthorizationManager implements AuthorizationManager<MethodInvocationResult> {
private final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
/**
* Sets the {@link MethodSecurityExpressionHandler}.
* Use this the {@link MethodSecurityExpressionHandler}.
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
@ -60,23 +58,23 @@ public final class PostAuthorizeAuthorizationManager implements AuthorizationMan
}
/**
* Determines if an {@link Authentication} has access to the {@link MethodInvocation}
* by evaluating an expression from the {@link PostAuthorize} annotation.
* Determine if an {@link Authentication} has access to the returned object by
* evaluating the {@link PostAuthorize} annotation that the {@link MethodInvocation}
* specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @return an {@link AuthorizationDecision} or null if the {@link PostAuthorize}
* annotation is not present
* @param mi the {@link MethodInvocationResult} to check
* @return an {@link AuthorizationDecision} or {@code null} if the
* {@link PostAuthorize} annotation is not present
*/
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext) {
ExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext);
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocationResult mi) {
ExpressionAttribute attribute = this.registry.getAttribute(mi.getMethodInvocation());
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
return null;
}
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(),
methodAuthorizationContext.getMethodInvocation());
this.expressionHandler.setReturnObject(methodAuthorizationContext.getReturnObject(), ctx);
mi.getMethodInvocation());
this.expressionHandler.setReturnObject(mi.getResult(), ctx);
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);
return new AuthorizationDecision(granted);
}
@ -98,9 +96,10 @@ public final class PostAuthorizeAuthorizationManager implements AuthorizationMan
}
private PostAuthorize findPostAuthorizeAnnotation(Method method) {
PostAuthorize postAuthorize = AnnotationUtils.findAnnotation(method, PostAuthorize.class);
return (postAuthorize != null) ? postAuthorize
: AnnotationUtils.findAnnotation(method.getDeclaringClass(), PostAuthorize.class);
PostAuthorize postAuthorize = AuthorizationAnnotationUtils.findUniqueAnnotation(method,
PostAuthorize.class);
return (postAuthorize != null) ? postAuthorize : AuthorizationAnnotationUtils
.findUniqueAnnotation(method.getDeclaringClass(), PostAuthorize.class);
}
}

View File

@ -19,48 +19,65 @@ package org.springframework.security.authorization.method;
import java.lang.reflect.Method;
import java.util.function.Supplier;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.StaticMethodMatcher;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.Ordered;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.lang.NonNull;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.method.AuthorizationMethodAfterAdvice;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationMethodAfterAdvice} which filters a <code>returnedObject</code>
* from the {@link MethodInvocation} by evaluating an expression from the
* {@link PostFilter} annotation.
* A {@link MethodInterceptor} which filters a {@code returnedObject} from the
* {@link MethodInvocation} by evaluating an expression from the {@link PostFilter}
* annotation.
*
* @author Evgeniy Cheban
* @since 5.5
* @author Josh Cummings
* @since 5.6
*/
public final class PostFilterAuthorizationMethodAfterAdvice
implements AuthorizationMethodAfterAdvice<MethodAuthorizationContext> {
public final class PostFilterAuthorizationMethodInterceptor
implements Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
private static final Supplier<Authentication> AUTHENTICATION_SUPPLIER = () -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new AuthenticationCredentialsNotFoundException(
"An Authentication object was not found in the SecurityContext");
}
return authentication;
};
private final PostFilterExpressionAttributeRegistry registry = new PostFilterExpressionAttributeRegistry();
private final MethodMatcher methodMatcher = new StaticMethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return PostFilterAuthorizationMethodAfterAdvice.this.registry.getAttribute(method,
targetClass) != ExpressionAttribute.NULL_ATTRIBUTE;
}
};
private int order = AuthorizationInterceptorsOrder.POST_FILTER.getOrder();
private final Pointcut pointcut;
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
/**
* Sets the {@link MethodSecurityExpressionHandler}.
* Creates a {@link PostFilterAuthorizationMethodInterceptor} using the provided
* parameters
*/
public PostFilterAuthorizationMethodInterceptor() {
this.pointcut = AuthorizationMethodPointcuts.forAnnotations(PostFilter.class);
}
/**
* Use this {@link MethodSecurityExpressionHandler}.
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
@ -68,35 +85,51 @@ public final class PostFilterAuthorizationMethodAfterAdvice
this.expressionHandler = expressionHandler;
}
/**
* {@inheritDoc}
*/
@Override
public MethodMatcher getMethodMatcher() {
return this.methodMatcher;
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
/**
* Filters a <code>returnedObject</code> from the {@link MethodInvocation} by
* evaluating an expression from the {@link PostFilter} annotation.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @param returnedObject the returned object from the {@link MethodInvocation} to
* check
* @return filtered <code>returnedObject</code> from the {@link MethodInvocation}
* {@inheritDoc}
*/
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext methodAuthorizationContext,
Object returnedObject) {
if (returnedObject == null) {
return null;
}
ExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext);
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public Advice getAdvice() {
return this;
}
@Override
public boolean isPerInstance() {
return true;
}
/**
* Filter a {@code returnedObject} using the {@link PostFilter} annotation that the
* {@link MethodInvocation} specifies.
* @param mi the {@link MethodInvocation} to check check
* @return filtered {@code returnedObject}
*/
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Object returnedObject = mi.proceed();
ExpressionAttribute attribute = this.registry.getAttribute(mi);
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
return returnedObject;
}
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(),
methodAuthorizationContext.getMethodInvocation());
Object result = this.expressionHandler.filter(returnedObject, attribute.getExpression(), ctx);
methodAuthorizationContext.setReturnObject(result);
return result;
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER.get(), mi);
return this.expressionHandler.filter(returnedObject, attribute.getExpression(), ctx);
}
private final class PostFilterExpressionAttributeRegistry
@ -110,15 +143,15 @@ public final class PostFilterAuthorizationMethodAfterAdvice
if (postFilter == null) {
return ExpressionAttribute.NULL_ATTRIBUTE;
}
Expression postFilterExpression = PostFilterAuthorizationMethodAfterAdvice.this.expressionHandler
Expression postFilterExpression = PostFilterAuthorizationMethodInterceptor.this.expressionHandler
.getExpressionParser().parseExpression(postFilter.value());
return new ExpressionAttribute(postFilterExpression);
}
private PostFilter findPostFilterAnnotation(Method method) {
PostFilter postFilter = AnnotationUtils.findAnnotation(method, PostFilter.class);
PostFilter postFilter = AuthorizationAnnotationUtils.findUniqueAnnotation(method, PostFilter.class);
return (postFilter != null) ? postFilter
: AnnotationUtils.findAnnotation(method.getDeclaringClass(), PostFilter.class);
: AuthorizationAnnotationUtils.findUniqueAnnotation(method.getDeclaringClass(), PostFilter.class);
}
}

View File

@ -23,13 +23,11 @@ import org.aopalliance.intercept.MethodInvocation;
import reactor.util.annotation.NonNull;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.security.access.expression.ExpressionUtils;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
@ -37,14 +35,14 @@ import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has
* access to the {@link MethodInvocation} by evaluating an expression from the
* An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* invoke the {@link MethodInvocation} by evaluating an expression from the
* {@link PreAuthorize} annotation.
*
* @author Evgeniy Cheban
* @since 5.5
* @since 5.6
*/
public final class PreAuthorizeAuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> {
public final class PreAuthorizeAuthorizationManager implements AuthorizationManager<MethodInvocation> {
private final PreAuthorizeExpressionAttributeRegistry registry = new PreAuthorizeExpressionAttributeRegistry();
@ -60,22 +58,21 @@ public final class PreAuthorizeAuthorizationManager implements AuthorizationMana
}
/**
* Determines if an {@link Authentication} has access to the {@link MethodInvocation}
* by evaluating an expression from the {@link PreAuthorize} annotation.
* Determine if an {@link Authentication} has access to a method by evaluating an
* expression from the {@link PreAuthorize} annotation that the
* {@link MethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @return an {@link AuthorizationDecision} or null if the {@link PreAuthorize}
* annotation is not present
* @param mi the {@link MethodInvocation} to check
* @return an {@link AuthorizationDecision} or {@code null} if the
* {@link PreAuthorize} annotation is not present
*/
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext) {
ExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext);
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) {
ExpressionAttribute attribute = this.registry.getAttribute(mi);
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
return null;
}
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(),
methodAuthorizationContext.getMethodInvocation());
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);
return new AuthorizationDecision(granted);
}
@ -97,9 +94,9 @@ public final class PreAuthorizeAuthorizationManager implements AuthorizationMana
}
private PreAuthorize findPreAuthorizeAnnotation(Method method) {
PreAuthorize preAuthorize = AnnotationUtils.findAnnotation(method, PreAuthorize.class);
PreAuthorize preAuthorize = AuthorizationAnnotationUtils.findUniqueAnnotation(method, PreAuthorize.class);
return (preAuthorize != null) ? preAuthorize
: AnnotationUtils.findAnnotation(method.getDeclaringClass(), PreAuthorize.class);
: AuthorizationAnnotationUtils.findUniqueAnnotation(method.getDeclaringClass(), PreAuthorize.class);
}
}

View File

@ -19,48 +19,65 @@ package org.springframework.security.authorization.method;
import java.lang.reflect.Method;
import java.util.function.Supplier;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.StaticMethodMatcher;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.Ordered;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.lang.NonNull;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.method.AuthorizationMethodBeforeAdvice;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.access.prepost.PreFilter;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* An {@link AuthorizationMethodBeforeAdvice} which filters a method argument by
* evaluating an expression from the {@link PreFilter} annotation.
* A {@link MethodInterceptor} which filters a method argument by evaluating an expression
* from the {@link PreFilter} annotation.
*
* @author Evgeniy Cheban
* @since 5.5
* @author Josh Cummings
* @since 5.6
*/
public final class PreFilterAuthorizationMethodBeforeAdvice
implements AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> {
public final class PreFilterAuthorizationMethodInterceptor
implements Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
private static final Supplier<Authentication> AUTHENTICATION_SUPPLIER = () -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
throw new AuthenticationCredentialsNotFoundException(
"An Authentication object was not found in the SecurityContext");
}
return authentication;
};
private final PreFilterExpressionAttributeRegistry registry = new PreFilterExpressionAttributeRegistry();
private final MethodMatcher methodMatcher = new StaticMethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return PreFilterAuthorizationMethodBeforeAdvice.this.registry.getAttribute(method,
targetClass) != PreFilterExpressionAttribute.NULL_ATTRIBUTE;
}
};
private int order = AuthorizationInterceptorsOrder.PRE_FILTER.getOrder();
private final Pointcut pointcut;
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
/**
* Sets the {@link MethodSecurityExpressionHandler}.
* Creates a {@link PreFilterAuthorizationMethodInterceptor} using the provided
* parameters
*/
public PreFilterAuthorizationMethodInterceptor() {
this.pointcut = AuthorizationMethodPointcuts.forAnnotations(PreFilter.class);
}
/**
* Use this {@link MethodSecurityExpressionHandler}
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
@ -68,27 +85,51 @@ public final class PreFilterAuthorizationMethodBeforeAdvice
this.expressionHandler = expressionHandler;
}
/**
* {@inheritDoc}
*/
@Override
public MethodMatcher getMethodMatcher() {
return this.methodMatcher;
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
/**
* Filters a method argument by evaluating an expression from the {@link PreFilter}
* annotation.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* {@inheritDoc}
*/
@Override
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext methodAuthorizationContext) {
PreFilterExpressionAttribute attribute = this.registry.getAttribute(methodAuthorizationContext);
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public Advice getAdvice() {
return this;
}
@Override
public boolean isPerInstance() {
return true;
}
/**
* Filter the method argument specified in the {@link PreFilter} annotation that
* {@link MethodInvocation} specifies.
* @param mi the {@link MethodInvocation} to check
*/
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
PreFilterExpressionAttribute attribute = this.registry.getAttribute(mi);
if (attribute == PreFilterExpressionAttribute.NULL_ATTRIBUTE) {
return;
return mi.proceed();
}
MethodInvocation mi = methodAuthorizationContext.getMethodInvocation();
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER.get(), mi);
Object filterTarget = findFilterTarget(attribute.filterTarget, ctx, mi);
this.expressionHandler.filter(filterTarget, attribute.getExpression(), ctx);
return mi.proceed();
}
private Object findFilterTarget(String filterTargetName, EvaluationContext ctx, MethodInvocation methodInvocation) {
@ -122,15 +163,15 @@ public final class PreFilterAuthorizationMethodBeforeAdvice
if (preFilter == null) {
return PreFilterExpressionAttribute.NULL_ATTRIBUTE;
}
Expression preFilterExpression = PreFilterAuthorizationMethodBeforeAdvice.this.expressionHandler
Expression preFilterExpression = PreFilterAuthorizationMethodInterceptor.this.expressionHandler
.getExpressionParser().parseExpression(preFilter.value());
return new PreFilterExpressionAttribute(preFilterExpression, preFilter.filterTarget());
}
private PreFilter findPreFilterAnnotation(Method method) {
PreFilter preFilter = AnnotationUtils.findAnnotation(method, PreFilter.class);
PreFilter preFilter = AuthorizationAnnotationUtils.findUniqueAnnotation(method, PreFilter.class);
return (preFilter != null) ? preFilter
: AnnotationUtils.findAnnotation(method.getDeclaringClass(), PreFilter.class);
: AuthorizationAnnotationUtils.findUniqueAnnotation(method.getDeclaringClass(), PreFilter.class);
}
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.security.access.annotation;
package org.springframework.security.authorization.method;
import java.lang.reflect.Method;
import java.util.function.Supplier;
@ -22,57 +22,53 @@ import java.util.function.Supplier;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.NonNull;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.authorization.AuthorityAuthorizationManager;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
/**
* An {@link AuthorizationManager} which can determine if an {@link Authentication} has
* access to the {@link MethodInvocation} by evaluating if the {@link Authentication}
* An {@link AuthorizationManager} which can determine if an {@link Authentication} may
* invoke the {@link MethodInvocation} by evaluating if the {@link Authentication}
* contains a specified authority from the Spring Security's {@link Secured} annotation.
*
* @author Evgeniy Cheban
* @since 5.5
* @since 5.6
*/
public final class SecuredAuthorizationManager implements AuthorizationManager<MethodAuthorizationContext> {
public final class SecuredAuthorizationManager implements AuthorizationManager<MethodInvocation> {
private final SecuredAuthorizationManagerRegistry registry = new SecuredAuthorizationManagerRegistry();
/**
* Determines if an {@link Authentication} has access to the {@link MethodInvocation}
* by evaluating if the {@link Authentication} contains a specified authority from the
* Spring Security's {@link Secured} annotation.
* Determine if an {@link Authentication} has access to a method by evaluating the
* {@link Secured} annotation that {@link MethodInvocation} specifies.
* @param authentication the {@link Supplier} of the {@link Authentication} to check
* @param methodAuthorizationContext the {@link MethodAuthorizationContext} to check
* @param mi the {@link MethodInvocation} to check
* @return an {@link AuthorizationDecision} or null if the {@link Secured} annotation
* is not present
*/
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext) {
AuthorizationManager<MethodAuthorizationContext> delegate = this.registry
.getManager(methodAuthorizationContext);
return delegate.check(authentication, methodAuthorizationContext);
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) {
AuthorizationManager<MethodInvocation> delegate = this.registry.getManager(mi);
return delegate.check(authentication, mi);
}
private static final class SecuredAuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry {
@NonNull
@Override
AuthorizationManager<MethodAuthorizationContext> resolveManager(Method method, Class<?> targetClass) {
AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass) {
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
Secured secured = findSecuredAnnotation(specificMethod);
return (secured != null) ? AuthorityAuthorizationManager.hasAnyAuthority(secured.value()) : NULL_MANAGER;
}
private Secured findSecuredAnnotation(Method method) {
Secured secured = AnnotationUtils.findAnnotation(method, Secured.class);
Secured secured = AuthorizationAnnotationUtils.findUniqueAnnotation(method, Secured.class);
return (secured != null) ? secured
: AnnotationUtils.findAnnotation(method.getDeclaringClass(), Secured.class);
: AuthorizationAnnotationUtils.findUniqueAnnotation(method.getDeclaringClass(), Secured.class);
}
}

View File

@ -60,4 +60,10 @@ public interface BusinessService extends Serializable {
List<?> methodReturningAList(String userName, String extraParam);
@RequireAdminRole
@RequireUserRole
default void repeatedAnnotations() {
}
}

View File

@ -1,167 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.annotation;
import java.util.function.Supplier;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import org.junit.Test;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.core.Authentication;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link Jsr250AuthorizationManager}.
*
* @author Evgeniy Cheban
*/
public class Jsr250AuthorizationManagerTests {
@Test
public void rolePrefixWhenNotSetThenDefaultsToRole() {
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
assertThat(manager).extracting("rolePrefix").isEqualTo("ROLE_");
}
@Test
public void setRolePrefixWhenNullThenException() {
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
assertThatIllegalArgumentException().isThrownBy(() -> manager.setRolePrefix(null))
.withMessage("rolePrefix cannot be null");
}
@Test
public void setRolePrefixWhenNotNullThenSets() {
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
manager.setRolePrefix("CUSTOM_");
assertThat(manager).extracting("rolePrefix").isEqualTo("CUSTOM_");
}
@Test
public void checkDoSomethingWhenNoJsr250AnnotationsThenNullDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
assertThat(decision).isNull();
}
@Test
public void checkPermitAllRolesAllowedAdminWhenRoleUserThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"permitAllRolesAllowedAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkDenyAllRolesAllowedAdminWhenRoleAdminThenDeniedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"denyAllRolesAllowedAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin,
methodAuthorizationContext);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}
@Test
public void checkRolesAllowedUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"rolesAllowedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkRolesAllowedUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"rolesAllowedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin,
methodAuthorizationContext);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkRolesAllowedUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
"ROLE_ANONYMOUS");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"rolesAllowedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, methodAuthorizationContext);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}
public static class TestClass {
public void doSomething() {
}
@DenyAll
@RolesAllowed("ADMIN")
public void denyAllRolesAllowedAdmin() {
}
@PermitAll
@RolesAllowed("ADMIN")
public void permitAllRolesAllowedAdmin() {
}
@RolesAllowed({ "USER", "ADMIN" })
public void rolesAllowedUserOrAdmin() {
}
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.security.RolesAllowed;
import org.springframework.security.access.prepost.PreAuthorize;
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole('ADMIN')")
@RolesAllowed("ADMIN")
@Secured("ADMIN")
public @interface RequireAdminRole {
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.annotation.security.RolesAllowed;
import org.springframework.security.access.prepost.PreAuthorize;
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole('USER')")
@RolesAllowed("ADMIN")
@Secured("USER")
public @interface RequireUserRole {
}

View File

@ -1,104 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.annotation;
import java.util.function.Supplier;
import org.junit.Test;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.core.Authentication;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SecuredAuthorizationManager}.
*
* @author Evgeniy Cheban
*/
public class SecuredAuthorizationManagerTests {
@Test
public void checkDoSomethingWhenNoSecuredAnnotationThenNullDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
assertThat(decision).isNull();
}
@Test
public void checkSecuredUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"securedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkSecuredUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"securedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin,
methodAuthorizationContext);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkSecuredUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
"ROLE_ANONYMOUS");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"securedUserOrAdmin");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(methodInvocation,
TestClass.class);
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, methodAuthorizationContext);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}
public static class TestClass {
public void doSomething() {
}
@Secured({ "ROLE_USER", "ROLE_ADMIN" })
public void securedUserOrAdmin() {
}
}
}

View File

@ -1,107 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.intercept.aopalliance;
import java.util.function.Supplier;
import org.junit.After;
import org.junit.Test;
import org.springframework.aop.MethodMatcher;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.access.method.AuthorizationMethodAfterAdvice;
import org.springframework.security.access.method.AuthorizationMethodBeforeAdvice;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/**
* Tests for {@link AuthorizationMethodInterceptor}.
*
* @author Evgeniy Cheban
*/
public class AuthorizationMethodInterceptorTests {
@After
public void tearDown() {
SecurityContextHolder.clearContext();
}
@Test
public void invokeWhenAuthenticatedThenVerifyAdvicesUsage() throws Throwable {
Authentication authentication = TestAuthentication.authenticatedUser();
SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString");
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> mockBeforeAdvice = mock(
AuthorizationMethodBeforeAdvice.class);
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> mockAfterAdvice = mock(
AuthorizationMethodAfterAdvice.class);
given(mockAfterAdvice.after(any(), any(MethodAuthorizationContext.class), eq(null))).willReturn("abc");
AuthorizationMethodInterceptor interceptor = new AuthorizationMethodInterceptor(mockBeforeAdvice,
mockAfterAdvice);
Object result = interceptor.invoke(mockMethodInvocation);
assertThat(result).isEqualTo("abc");
verify(mockAfterAdvice).after(any(), any(MethodAuthorizationContext.class), eq(null));
}
@Test
public void invokeWhenNotAuthenticatedThenAuthenticationCredentialsNotFoundException() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString");
AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> beforeAdvice = new AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>() {
@Override
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
@Override
public void before(Supplier<Authentication> authentication,
MethodAuthorizationContext methodAuthorizationContext) {
authentication.get();
}
};
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> mockAfterAdvice = mock(
AuthorizationMethodAfterAdvice.class);
AuthorizationMethodInterceptor interceptor = new AuthorizationMethodInterceptor(beforeAdvice, mockAfterAdvice);
assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
.isThrownBy(() -> interceptor.invoke(mockMethodInvocation))
.withMessage("An Authentication object was not found in the SecurityContext");
verifyNoInteractions(mockAfterAdvice);
}
public static class TestClass {
public String doSomethingString() {
return null;
}
}
}

View File

@ -1,164 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.method;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import org.junit.Test;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.support.StaticMethodMatcher;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.core.Authentication;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DelegatingAuthorizationMethodAfterAdvice}.
*
* @author Evgeniy Cheban
*/
public class DelegatingAuthorizationMethodAfterAdviceTests {
@Test
public void methodMatcherWhenNoneMatchesThenNotMatches() throws Exception {
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> delegates = new ArrayList<>();
delegates.add(new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext object,
Object returnedObject) {
return returnedObject;
}
@Override
public MethodMatcher getMethodMatcher() {
return new StaticMethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
};
}
});
delegates.add(new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext object,
Object returnedObject) {
return returnedObject;
}
@Override
public MethodMatcher getMethodMatcher() {
return new StaticMethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
};
}
});
DelegatingAuthorizationMethodAfterAdvice advice = new DelegatingAuthorizationMethodAfterAdvice(delegates);
MethodMatcher methodMatcher = advice.getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomething"), TestClass.class)).isFalse();
}
@Test
public void methodMatcherWhenAnyMatchesThenMatches() throws Exception {
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> delegates = new ArrayList<>();
delegates.add(new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext object,
Object returnedObject) {
return returnedObject;
}
@Override
public MethodMatcher getMethodMatcher() {
return new StaticMethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
};
}
});
delegates.add(new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext object,
Object returnedObject) {
return returnedObject;
}
@Override
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
});
DelegatingAuthorizationMethodAfterAdvice advice = new DelegatingAuthorizationMethodAfterAdvice(delegates);
MethodMatcher methodMatcher = advice.getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomething"), TestClass.class)).isTrue();
}
@Test
public void checkWhenDelegatingAdviceModifiesReturnedObjectThenModifiedReturnedObject() throws Exception {
List<AuthorizationMethodAfterAdvice<MethodAuthorizationContext>> delegates = new ArrayList<>();
delegates.add(new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext object,
Object returnedObject) {
return returnedObject + "b";
}
@Override
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
});
delegates.add(new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
@Override
public Object after(Supplier<Authentication> authentication, MethodAuthorizationContext object,
Object returnedObject) {
return returnedObject + "c";
}
@Override
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
});
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
DelegatingAuthorizationMethodAfterAdvice advice = new DelegatingAuthorizationMethodAfterAdvice(delegates);
Object result = advice.after(TestAuthentication::authenticatedUser, methodAuthorizationContext, "a");
assertThat(result).isEqualTo("abc");
}
public static class TestClass {
public String doSomething() {
return null;
}
}
}

View File

@ -1,168 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.access.method;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import org.junit.Test;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.support.StaticMethodMatcher;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.core.Authentication;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for {@link DelegatingAuthorizationMethodBeforeAdvice}.
*
* @author Evgeniy Cheban
*/
public class DelegatingAuthorizationMethodBeforeAdviceTests {
@Test
public void methodMatcherWhenNoneMatchesThenNotMatches() throws Exception {
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> delegates = new ArrayList<>();
delegates.add(new AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>() {
@Override
public MethodMatcher getMethodMatcher() {
return new StaticMethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
};
}
@Override
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext object) {
}
});
delegates.add(new AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>() {
@Override
public MethodMatcher getMethodMatcher() {
return new StaticMethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
};
}
@Override
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext object) {
}
});
DelegatingAuthorizationMethodBeforeAdvice advice = new DelegatingAuthorizationMethodBeforeAdvice(delegates);
MethodMatcher methodMatcher = advice.getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomething"), TestClass.class)).isFalse();
}
@Test
public void methodMatcherWhenAnyMatchesThenMatches() throws Exception {
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> delegates = new ArrayList<>();
delegates.add(new AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>() {
@Override
public MethodMatcher getMethodMatcher() {
return new StaticMethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return false;
}
};
}
@Override
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext object) {
}
});
delegates.add(new AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>() {
@Override
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
@Override
public void before(Supplier<Authentication> authentication, MethodAuthorizationContext object) {
}
});
DelegatingAuthorizationMethodBeforeAdvice advice = new DelegatingAuthorizationMethodBeforeAdvice(delegates);
MethodMatcher methodMatcher = advice.getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomething"), TestClass.class)).isTrue();
}
@Test
public void checkWhenAllGrantsOrAbstainsThenPasses() throws Exception {
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> delegates = new ArrayList<>();
delegates.add(new AuthorizationManagerMethodBeforeAdvice<>(MethodMatcher.TRUE, (a, o) -> null));
delegates.add(new AuthorizationManagerMethodBeforeAdvice<>(MethodMatcher.TRUE,
(a, o) -> new AuthorizationDecision(true)));
delegates.add(new AuthorizationManagerMethodBeforeAdvice<>(MethodMatcher.TRUE, (a, o) -> null));
DelegatingAuthorizationMethodBeforeAdvice advice = new DelegatingAuthorizationMethodBeforeAdvice(delegates);
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext);
}
@Test
public void checkWhenAnyDeniesThenAccessDeniedException() throws Exception {
List<AuthorizationMethodBeforeAdvice<MethodAuthorizationContext>> delegates = new ArrayList<>();
delegates.add(new AuthorizationManagerMethodBeforeAdvice<>(MethodMatcher.TRUE, (a, o) -> null));
delegates.add(new AuthorizationManagerMethodBeforeAdvice<>(MethodMatcher.TRUE,
(a, o) -> new AuthorizationDecision(true)));
delegates.add(new AuthorizationManagerMethodBeforeAdvice<>(MethodMatcher.TRUE,
(a, o) -> new AuthorizationDecision(false)));
DelegatingAuthorizationMethodBeforeAdvice advice = new DelegatingAuthorizationMethodBeforeAdvice(delegates);
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext))
.withMessage("Access Denied");
}
@Test
public void checkWhenDelegatesEmptyThenPasses() throws Exception {
DelegatingAuthorizationMethodBeforeAdvice advice = new DelegatingAuthorizationMethodBeforeAdvice(
Collections.emptyList());
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething");
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext);
}
public static class TestClass {
public void doSomething() {
}
}
}

View File

@ -14,55 +14,56 @@
* limitations under the License.
*/
package org.springframework.security.access.method;
import java.util.function.Supplier;
package org.springframework.security.authorization.method;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
import org.springframework.aop.MethodMatcher;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.aop.Pointcut;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link AuthorizationManagerMethodAfterAdvice}.
* Tests for {@link AuthorizationManagerAfterMethodInterceptor}.
*
* @author Evgeniy Cheban
*/
public class AuthorizationManagerMethodAfterAdviceTests {
public class AuthorizationManagerAfterMethodInterceptorTests {
@Test
public void instantiateWhenMethodMatcherNullThenException() {
AuthorizationManager<MethodInvocationResult> mockAuthorizationManager = mock(AuthorizationManager.class);
assertThatIllegalArgumentException()
.isThrownBy(() -> new AuthorizationManagerMethodAfterAdvice<>(null, mock(AuthorizationManager.class)))
.withMessage("methodMatcher cannot be null");
.isThrownBy(() -> new AuthorizationManagerAfterMethodInterceptor(null, mockAuthorizationManager))
.withMessage("pointcut cannot be null");
}
@Test
public void instantiateWhenAuthorizationManagerNullThenException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new AuthorizationManagerMethodAfterAdvice<>(mock(MethodMatcher.class), null))
.isThrownBy(() -> new AuthorizationManagerAfterMethodInterceptor(mock(Pointcut.class), null))
.withMessage("authorizationManager cannot be null");
}
@Test
public void beforeWhenMockAuthorizationManagerThenVerifyAndReturnedObject() {
Supplier<Authentication> authentication = TestAuthentication::authenticatedUser;
public void beforeWhenMockAuthorizationManagerThenVerifyAndReturnedObject() throws Throwable {
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
Object returnedObject = new Object();
AuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(AuthorizationManager.class);
AuthorizationManagerMethodAfterAdvice<MethodInvocation> advice = new AuthorizationManagerMethodAfterAdvice<>(
mock(MethodMatcher.class), mockAuthorizationManager);
Object result = advice.after(authentication, mockMethodInvocation, returnedObject);
assertThat(result).isEqualTo(returnedObject);
verify(mockAuthorizationManager).verify(authentication, mockMethodInvocation);
MethodInvocationResult result = new MethodInvocationResult(mockMethodInvocation, new Object());
given(mockMethodInvocation.proceed()).willReturn(result.getResult());
AuthorizationManager<MethodInvocationResult> mockAuthorizationManager = mock(AuthorizationManager.class);
AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(
Pointcut.TRUE, mockAuthorizationManager);
Object returnedObject = advice.invoke(mockMethodInvocation);
assertThat(returnedObject).isEqualTo(result.getResult());
verify(mockAuthorizationManager).verify(eq(AuthorizationManagerAfterMethodInterceptor.AUTHENTICATION_SUPPLIER),
any(MethodInvocationResult.class));
}
}

View File

@ -14,52 +14,49 @@
* limitations under the License.
*/
package org.springframework.security.access.method;
import java.util.function.Supplier;
package org.springframework.security.authorization.method;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
import org.springframework.aop.MethodMatcher;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.aop.Pointcut;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link AuthorizationManagerMethodBeforeAdvice}.
* Tests for {@link AuthorizationManagerBeforeMethodInterceptor}.
*
* @author Evgeniy Cheban
*/
public class AuthorizationManagerMethodBeforeAdviceTests {
public class AuthorizationManagerBeforeMethodInterceptorTests {
@Test
public void instantiateWhenMethodMatcherNullThenException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new AuthorizationManagerMethodBeforeAdvice<>(null, mock(AuthorizationManager.class)))
.withMessage("methodMatcher cannot be null");
.isThrownBy(
() -> new AuthorizationManagerBeforeMethodInterceptor(null, mock(AuthorizationManager.class)))
.withMessage("pointcut cannot be null");
}
@Test
public void instantiateWhenAuthorizationManagerNullThenException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new AuthorizationManagerMethodBeforeAdvice<>(mock(MethodMatcher.class), null))
.isThrownBy(() -> new AuthorizationManagerBeforeMethodInterceptor(mock(Pointcut.class), null))
.withMessage("authorizationManager cannot be null");
}
@Test
public void beforeWhenMockAuthorizationManagerThenVerify() {
Supplier<Authentication> authentication = TestAuthentication::authenticatedUser;
public void beforeWhenMockAuthorizationManagerThenVerify() throws Throwable {
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
AuthorizationManager<MethodInvocation> mockAuthorizationManager = mock(AuthorizationManager.class);
AuthorizationManagerMethodBeforeAdvice<MethodInvocation> advice = new AuthorizationManagerMethodBeforeAdvice<>(
mock(MethodMatcher.class), mockAuthorizationManager);
advice.before(authentication, mockMethodInvocation);
verify(mockAuthorizationManager).verify(authentication, mockMethodInvocation);
AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(
Pointcut.TRUE, mockAuthorizationManager);
advice.invoke(mockMethodInvocation);
verify(mockAuthorizationManager).verify(AuthorizationManagerBeforeMethodInterceptor.AUTHENTICATION_SUPPLIER,
mockMethodInvocation);
}
}

View File

@ -0,0 +1,167 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.authorization.method;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.Test;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AopUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link AuthorizationMethodPointcuts}
*/
public class AuthorizationMethodPointcutsTests {
@Test
public void forAnnotationsWhenAnnotationThenClassBasedAnnotationPointcut() {
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
assertThat(AopUtils.canApply(preAuthorize, ClassController.class)).isTrue();
assertThat(AopUtils.canApply(preAuthorize, NoController.class)).isFalse();
}
@Test
public void forAnnotationsWhenAnnotationThenMethodBasedAnnotationPointcut() {
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
assertThat(AopUtils.canApply(preAuthorize, MethodController.class)).isTrue();
}
@Test
public void forAnnotationsWhenAnnotationThenClassInheritancePointcut() {
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
assertThat(AopUtils.canApply(preAuthorize, InterfacedClassController.class)).isTrue();
}
@Test
public void forAnnotationsWhenAnnotationThenMethodInheritancePointcut() {
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
assertThat(AopUtils.canApply(preAuthorize, InterfacedMethodController.class)).isTrue();
}
@Test
public void forAnnotationsWhenAnnotationThenAnnotationClassInheritancePointcut() {
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
assertThat(AopUtils.canApply(preAuthorize, InterfacedAnnotationClassController.class)).isTrue();
}
@Test
public void forAnnotationsWhenAnnotationThenAnnotationMethodInheritancePointcut() {
Pointcut preAuthorize = AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class);
assertThat(AopUtils.canApply(preAuthorize, InterfacedAnnotationMethodController.class)).isTrue();
}
@PreAuthorize("hasAuthority('APP')")
public static class ClassController {
String methodOne(String paramOne) {
return "value";
}
}
public static class MethodController {
@PreAuthorize("hasAuthority('APP')")
String methodOne(String paramOne) {
return "value";
}
}
public static class NoController {
String methodOne(String paramOne) {
return "value";
}
}
@PreAuthorize("hasAuthority('APP')")
public interface ClassControllerInterface {
String methodOne(String paramOne);
}
public static class InterfacedClassController implements ClassControllerInterface {
public String methodOne(String paramOne) {
return "value";
}
}
public interface MethodControllerInterface {
@PreAuthorize("hasAuthority('APP')")
String methodOne(String paramOne);
}
public static class InterfacedMethodController implements MethodControllerInterface {
public String methodOne(String paramOne) {
return "value";
}
}
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasAuthority('APP')")
@interface MyAnnotation {
}
@MyAnnotation
public interface ClassAnnotationControllerInterface {
String methodOne(String paramOne);
}
public static class InterfacedAnnotationClassController implements ClassAnnotationControllerInterface {
public String methodOne(String paramOne) {
return "value";
}
}
public interface MethodAnnotationControllerInterface {
@MyAnnotation
String methodOne(String paramOne);
}
public static class InterfacedAnnotationMethodController implements MethodAnnotationControllerInterface {
public String methodOne(String paramOne) {
return "value";
}
}
}

View File

@ -0,0 +1,278 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.authorization.method;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.function.Supplier;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import org.junit.Test;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.core.Authentication;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link Jsr250AuthorizationManager}.
*
* @author Evgeniy Cheban
*/
public class Jsr250AuthorizationManagerTests {
@Test
public void rolePrefixWhenNotSetThenDefaultsToRole() {
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
assertThat(manager).extracting("rolePrefix").isEqualTo("ROLE_");
}
@Test
public void setRolePrefixWhenNullThenException() {
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
assertThatIllegalArgumentException().isThrownBy(() -> manager.setRolePrefix(null))
.withMessage("rolePrefix cannot be null");
}
@Test
public void setRolePrefixWhenNotNullThenSets() {
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
manager.setRolePrefix("CUSTOM_");
assertThat(manager).extracting("rolePrefix").isEqualTo("CUSTOM_");
}
@Test
public void checkDoSomethingWhenNoJsr250AnnotationsThenNullDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething");
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNull();
}
@Test
public void checkPermitAllWhenRoleUserThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, "permitAll");
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkDenyAllWhenRoleAdminThenDeniedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, "denyAll");
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}
@Test
public void checkRolesAllowedUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"rolesAllowedUserOrAdmin");
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkRolesAllowedUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"rolesAllowedUserOrAdmin");
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkRolesAllowedUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
"ROLE_ANONYMOUS");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"rolesAllowedUserOrAdmin");
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}
@Test
public void checkMultipleAnnotationsWhenInvokedThenAnnotationConfigurationException() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
"ROLE_ANONYMOUS");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"multipleAnnotations");
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> manager.check(authentication, methodInvocation));
}
@Test
public void checkRequiresAdminWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),
ClassLevelAnnotations.class, "rolesAllowedAdmin");
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, methodInvocation);
assertThat(decision.isGranted()).isFalse();
authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN");
decision = manager.check(authentication, methodInvocation);
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkDeniedWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),
ClassLevelAnnotations.class, "denyAll");
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, methodInvocation);
assertThat(decision.isGranted()).isFalse();
}
@Test
public void checkRequiresUserWhenClassAnnotationsThenApplies() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),
ClassLevelAnnotations.class, "rolesAllowedUser");
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, methodInvocation);
assertThat(decision.isGranted()).isTrue();
authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN");
decision = manager.check(authentication, methodInvocation);
assertThat(decision.isGranted()).isFalse();
}
@Test
public void checkInheritedAnnotationsWhenDuplicatedThenAnnotationConfigurationException() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"inheritedAnnotations");
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> manager.check(authentication, methodInvocation));
}
@Test
public void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),
ClassLevelAnnotations.class, "inheritedAnnotations");
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> manager.check(authentication, methodInvocation));
}
public static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {
public void doSomething() {
}
@DenyAll
public void denyAll() {
}
@PermitAll
public void permitAll() {
}
@RolesAllowed({ "USER", "ADMIN" })
public void rolesAllowedUserOrAdmin() {
}
@RolesAllowed("USER")
@DenyAll
public void multipleAnnotations() {
}
public void inheritedAnnotations() {
}
}
@RolesAllowed("USER")
public static class ClassLevelAnnotations implements InterfaceAnnotationsThree {
@RolesAllowed("ADMIN")
public void rolesAllowedAdmin() {
}
@DenyAll
public void denyAll() {
}
public void rolesAllowedUser() {
}
@Override
@PermitAll
public void inheritedAnnotations() {
}
}
public interface InterfaceAnnotationsOne {
@RolesAllowed("ADMIN")
void inheritedAnnotations();
}
public interface InterfaceAnnotationsTwo {
@MyRolesAllowed
void inheritedAnnotations();
}
public interface InterfaceAnnotationsThree {
@DenyAll
void inheritedAnnotations();
}
@Retention(RetentionPolicy.RUNTIME)
@RolesAllowed("USER")
public @interface MyRolesAllowed {
}
}

View File

@ -16,21 +16,27 @@
package org.springframework.security.authorization.method;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import org.junit.Test;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.core.Authentication;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
@ -57,38 +63,32 @@ public class PostAuthorizeAuthorizationManagerTests {
@Test
public void checkDoSomethingWhenNoPostAuthorizeAnnotationThenNullDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething", new Class[] {}, new Object[] {});
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
MethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, result);
assertThat(decision).isNull();
}
@Test
public void checkDoSomethingStringWhenArgIsGrantThenGrantedDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString", new Class[] { String.class }, new Object[] { "grant" });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
MethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, result);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString", new Class[] { String.class }, new Object[] { "deny" });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
MethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, result);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}
@ -96,14 +96,11 @@ public class PostAuthorizeAuthorizationManagerTests {
@Test
public void checkDoSomethingListWhenReturnObjectContainsGrantThenGrantedDecision() throws Exception {
List<String> list = Arrays.asList("grant", "deny");
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingList", new Class[] { List.class }, new Object[] { list });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
methodAuthorizationContext.setReturnObject(list);
MethodInvocationResult result = new MethodInvocationResult(methodInvocation, list);
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, result);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@ -111,19 +108,66 @@ public class PostAuthorizeAuthorizationManagerTests {
@Test
public void checkDoSomethingListWhenReturnObjectNotContainsGrantThenDeniedDecision() throws Exception {
List<String> list = Collections.singletonList("deny");
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingList", new Class[] { List.class }, new Object[] { list });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
methodAuthorizationContext.setReturnObject(list);
MethodInvocationResult result = new MethodInvocationResult(methodInvocation, list);
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, result);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}
public static class TestClass {
@Test
public void checkRequiresAdminWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),
ClassLevelAnnotations.class, "securedAdmin");
MethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, result);
assertThat(decision.isGranted()).isFalse();
authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN");
decision = manager.check(authentication, result);
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkRequiresUserWhenClassAnnotationsThenApplies() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),
ClassLevelAnnotations.class, "securedUser");
MethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, result);
assertThat(decision.isGranted()).isTrue();
authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN");
decision = manager.check(authentication, result);
assertThat(decision.isGranted()).isFalse();
}
@Test
public void checkInheritedAnnotationsWhenDuplicatedThenAnnotationConfigurationException() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"inheritedAnnotations");
MethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> manager.check(authentication, result));
}
@Test
public void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),
ClassLevelAnnotations.class, "inheritedAnnotations");
MethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> manager.check(authentication, result));
}
public static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {
public void doSomething() {
@ -139,6 +183,58 @@ public class PostAuthorizeAuthorizationManagerTests {
return list;
}
@Override
public void inheritedAnnotations() {
}
}
@PostAuthorize("hasRole('USER')")
public static class ClassLevelAnnotations implements InterfaceAnnotationsThree {
@PostAuthorize("hasRole('ADMIN')")
public void securedAdmin() {
}
public void securedUser() {
}
@Override
@PostAuthorize("hasRole('ADMIN')")
public void inheritedAnnotations() {
}
}
public interface InterfaceAnnotationsOne {
@PostAuthorize("hasRole('ADMIN')")
void inheritedAnnotations();
}
public interface InterfaceAnnotationsTwo {
@PostAuthorize("hasRole('USER')")
void inheritedAnnotations();
}
public interface InterfaceAnnotationsThree {
@MyPostAuthorize
void inheritedAnnotations();
}
@Retention(RetentionPolicy.RUNTIME)
@PostAuthorize("hasRole('USER')")
public @interface MyPostAuthorize {
}
}

View File

@ -1,96 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.authorization.method;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.Test;
import org.springframework.aop.MethodMatcher;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.authentication.TestAuthentication;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link PostFilterAuthorizationMethodAfterAdvice}.
*
* @author Evgeniy Cheban
*/
public class PostFilterAuthorizationMethodAfterAdviceTests {
@Test
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice();
advice.setExpressionHandler(expressionHandler);
assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler);
}
@Test
public void setExpressionHandlerWhenNullThenException() {
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice();
assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null))
.withMessage("expressionHandler cannot be null");
}
@Test
public void methodMatcherWhenMethodHasNotPostFilterAnnotationThenNotMatches() throws Exception {
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice();
MethodMatcher methodMatcher = advice.getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomething"), TestClass.class)).isFalse();
}
@Test
public void methodMatcherWhenMethodHasPostFilterAnnotationThenMatches() throws Exception {
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice();
MethodMatcher methodMatcher = advice.getMethodMatcher();
assertThat(
methodMatcher.matches(TestClass.class.getMethod("doSomethingArray", String[].class), TestClass.class))
.isTrue();
}
@Test
public void afterWhenArrayNotNullThenFilteredArray() throws Exception {
String[] array = { "john", "bob" };
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingArray", new Class[] { String[].class }, new Object[] { array });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PostFilterAuthorizationMethodAfterAdvice advice = new PostFilterAuthorizationMethodAfterAdvice();
Object result = advice.after(TestAuthentication::authenticatedUser, methodAuthorizationContext, array);
assertThat(result).asInstanceOf(InstanceOfAssertFactories.array(String[].class)).containsOnly("john");
}
public static class TestClass {
public void doSomething() {
}
@PostFilter("filterObject == 'john'")
public String[] doSomethingArray(String[] array) {
return array;
}
}
}

View File

@ -0,0 +1,186 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.authorization.method;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.aop.MethodMatcher;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.core.context.SecurityContextHolder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link PostFilterAuthorizationMethodInterceptor}.
*
* @author Evgeniy Cheban
*/
public class PostFilterAuthorizationMethodInterceptorTests {
@Before
public void setUp() {
SecurityContextHolder.getContext().setAuthentication(TestAuthentication.authenticatedUser());
}
@After
public void tearDown() {
SecurityContextHolder.clearContext();
}
@Test
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
advice.setExpressionHandler(expressionHandler);
assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler);
}
@Test
public void setExpressionHandlerWhenNullThenException() {
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null))
.withMessage("expressionHandler cannot be null");
}
@Test
public void methodMatcherWhenMethodHasNotPostFilterAnnotationThenNotMatches() throws Exception {
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
assertThat(methodMatcher.matches(NoPostFilterClass.class.getMethod("doSomething"), NoPostFilterClass.class))
.isFalse();
}
@Test
public void methodMatcherWhenMethodHasPostFilterAnnotationThenMatches() throws Exception {
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
assertThat(
methodMatcher.matches(TestClass.class.getMethod("doSomethingArray", String[].class), TestClass.class))
.isTrue();
}
@Test
public void afterWhenArrayNotNullThenFilteredArray() throws Throwable {
String[] array = { "john", "bob" };
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingArrayClassLevel", new Class[] { String[].class }, new Object[] { array }) {
@Override
public Object proceed() {
return array;
}
};
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
Object result = advice.invoke(methodInvocation);
assertThat(result).asInstanceOf(InstanceOfAssertFactories.array(String[].class)).containsOnly("john");
}
@Test
public void checkInheritedAnnotationsWhenDuplicatedThenAnnotationConfigurationException() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"inheritedAnnotations");
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> advice.invoke(methodInvocation));
}
@Test
public void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ConflictingAnnotations(),
ConflictingAnnotations.class, "inheritedAnnotations");
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> advice.invoke(methodInvocation));
}
@PostFilter("filterObject == 'john'")
public static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {
@PostFilter("filterObject == 'john'")
public String[] doSomethingArray(String[] array) {
return array;
}
public String[] doSomethingArrayClassLevel(String[] array) {
return array;
}
@Override
public void inheritedAnnotations() {
}
}
public static class NoPostFilterClass {
public void doSomething() {
}
}
public static class ConflictingAnnotations implements InterfaceAnnotationsThree {
@Override
@PostFilter("filterObject == 'jack'")
public void inheritedAnnotations() {
}
}
public interface InterfaceAnnotationsOne {
@PostFilter("filterObject == 'jim'")
void inheritedAnnotations();
}
public interface InterfaceAnnotationsTwo {
@PostFilter("filterObject == 'jane'")
void inheritedAnnotations();
}
public interface InterfaceAnnotationsThree {
@MyPostFilter
void inheritedAnnotations();
}
@Retention(RetentionPolicy.RUNTIME)
@PostFilter("filterObject == 'john'")
public @interface MyPostFilter {
}
}

View File

@ -16,17 +16,24 @@
package org.springframework.security.authorization.method;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.function.Supplier;
import org.junit.Test;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.core.Authentication;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
@ -53,43 +60,80 @@ public class PreAuthorizeAuthorizationManagerTests {
@Test
public void checkDoSomethingWhenNoPostAuthorizeAnnotationThenNullDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething", new Class[] {}, new Object[] {});
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNull();
}
@Test
public void checkDoSomethingStringWhenArgIsGrantThenGrantedDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString", new Class[] { String.class }, new Object[] { "grant" });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkDoSomethingStringWhenArgIsNotGrantThenDeniedDecision() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingString", new Class[] { String.class }, new Object[] { "deny" });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser,
methodAuthorizationContext);
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}
public static class TestClass {
@Test
public void checkRequiresAdminWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),
ClassLevelAnnotations.class, "securedAdmin");
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, methodInvocation);
assertThat(decision.isGranted()).isFalse();
authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN");
decision = manager.check(authentication, methodInvocation);
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkRequiresUserWhenClassAnnotationsThenApplies() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),
ClassLevelAnnotations.class, "securedUser");
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, methodInvocation);
assertThat(decision.isGranted()).isTrue();
authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN");
decision = manager.check(authentication, methodInvocation);
assertThat(decision.isGranted()).isFalse();
}
@Test
public void checkInheritedAnnotationsWhenDuplicatedThenAnnotationConfigurationException() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"inheritedAnnotations");
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> manager.check(authentication, methodInvocation));
}
@Test
public void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),
ClassLevelAnnotations.class, "inheritedAnnotations");
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> manager.check(authentication, methodInvocation));
}
public static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {
public void doSomething() {
@ -100,6 +144,58 @@ public class PreAuthorizeAuthorizationManagerTests {
return s;
}
@Override
public void inheritedAnnotations() {
}
}
@PreAuthorize("hasRole('USER')")
public static class ClassLevelAnnotations implements InterfaceAnnotationsThree {
@PreAuthorize("hasRole('ADMIN')")
public void securedAdmin() {
}
public void securedUser() {
}
@Override
@PreAuthorize("hasRole('ADMIN')")
public void inheritedAnnotations() {
}
}
public interface InterfaceAnnotationsOne {
@PreAuthorize("hasRole('ADMIN')")
void inheritedAnnotations();
}
public interface InterfaceAnnotationsTwo {
@PreAuthorize("hasRole('USER')")
void inheritedAnnotations();
}
public interface InterfaceAnnotationsThree {
@MyPreAuthorize
void inheritedAnnotations();
}
@Retention(RetentionPolicy.RUNTIME)
@PreAuthorize("hasRole('USER')")
public @interface MyPreAuthorize {
}
}

View File

@ -1,200 +0,0 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.authorization.method;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.springframework.aop.MethodMatcher;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.access.method.MethodAuthorizationContext;
import org.springframework.security.access.prepost.PreFilter;
import org.springframework.security.authentication.TestAuthentication;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link PreFilterAuthorizationMethodBeforeAdvice}.
*
* @author Evgeniy Cheban
*/
public class PreFilterAuthorizationMethodBeforeAdviceTests {
@Test
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice();
advice.setExpressionHandler(expressionHandler);
assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler);
}
@Test
public void setExpressionHandlerWhenNullThenException() {
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice();
assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null))
.withMessage("expressionHandler cannot be null");
}
@Test
public void methodMatcherWhenMethodHasNotPreFilterAnnotationThenNotMatches() throws Exception {
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice();
MethodMatcher methodMatcher = advice.getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomething"), TestClass.class)).isFalse();
}
@Test
public void methodMatcherWhenMethodHasPreFilterAnnotationThenMatches() throws Exception {
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice();
MethodMatcher methodMatcher = advice.getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomethingListFilterTargetMatch", List.class),
TestClass.class)).isTrue();
}
@Test
public void findFilterTargetWhenNameProvidedAndNotMatchThenException() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetNotMatch", new Class[] { List.class }, new Object[] { new ArrayList<>() });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice();
assertThatIllegalArgumentException()
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext))
.withMessage(
"Filter target was null, or no argument with name 'filterTargetNotMatch' found in method.");
}
@Test
public void findFilterTargetWhenNameProvidedAndMatchAndNullThenException() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { null });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice();
assertThatIllegalArgumentException()
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext))
.withMessage("Filter target was null, or no argument with name 'list' found in method.");
}
@Test
public void findFilterTargetWhenNameProvidedAndMatchAndNotNullThenFiltersList() throws Exception {
List<String> list = new ArrayList<>();
list.add("john");
list.add("bob");
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { list });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice();
advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext);
assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo("john");
}
@Test
public void findFilterTargetWhenNameNotProvidedAndSingleArgListNullThenException() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { null });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice();
assertThatIllegalArgumentException()
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext))
.withMessage("Filter target was null. Make sure you passing the correct value in the method argument.");
}
@Test
public void findFilterTargetWhenNameNotProvidedAndSingleArgListThenFiltersList() throws Exception {
List<String> list = new ArrayList<>();
list.add("john");
list.add("bob");
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { list });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice();
advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext);
assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo("john");
}
@Test
public void findFilterTargetWhenNameNotProvidedAndSingleArgArrayThenException() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingArrayFilterTargetNotProvided", new Class[] { String[].class },
new Object[] { new String[] {} });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice();
assertThatIllegalStateException()
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext))
.withMessage(
"Pre-filtering on array types is not supported. Using a Collection will solve this problem.");
}
@Test
public void findFilterTargetWhenNameNotProvidedAndNotSingleArgThenException() throws Exception {
MockMethodInvocation mockMethodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingTwoArgsFilterTargetNotProvided", new Class[] { String.class, List.class },
new Object[] { "", new ArrayList<>() });
MethodAuthorizationContext methodAuthorizationContext = new MethodAuthorizationContext(mockMethodInvocation,
TestClass.class);
PreFilterAuthorizationMethodBeforeAdvice advice = new PreFilterAuthorizationMethodBeforeAdvice();
assertThatIllegalStateException()
.isThrownBy(() -> advice.before(TestAuthentication::authenticatedUser, methodAuthorizationContext))
.withMessage("Unable to determine the method argument for filtering. Specify the filter target.");
}
public static class TestClass {
public void doSomething() {
}
@PreFilter(value = "filterObject == 'john'", filterTarget = "filterTargetNotMatch")
public List<String> doSomethingListFilterTargetNotMatch(List<String> list) {
return list;
}
@PreFilter(value = "filterObject == 'john'", filterTarget = "list")
public List<String> doSomethingListFilterTargetMatch(List<String> list) {
return list;
}
@PreFilter("filterObject == 'john'")
public List<String> doSomethingListFilterTargetNotProvided(List<String> list) {
return list;
}
@PreFilter("filterObject == 'john'")
public String[] doSomethingArrayFilterTargetNotProvided(String[] array) {
return array;
}
@PreFilter("filterObject == 'john'")
public List<String> doSomethingTwoArgsFilterTargetNotProvided(String s, List<String> list) {
return list;
}
}
}

View File

@ -0,0 +1,260 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.authorization.method;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.aop.MethodMatcher;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.access.prepost.PreFilter;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.core.context.SecurityContextHolder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/**
* Tests for {@link PreFilterAuthorizationMethodInterceptor}.
*
* @author Evgeniy Cheban
*/
public class PreFilterAuthorizationMethodInterceptorTests {
@Before
public void setUp() {
SecurityContextHolder.getContext().setAuthentication(TestAuthentication.authenticatedUser());
}
@After
public void tearDown() {
SecurityContextHolder.clearContext();
}
@Test
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
advice.setExpressionHandler(expressionHandler);
assertThat(advice).extracting("expressionHandler").isEqualTo(expressionHandler);
}
@Test
public void setExpressionHandlerWhenNullThenException() {
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
assertThatIllegalArgumentException().isThrownBy(() -> advice.setExpressionHandler(null))
.withMessage("expressionHandler cannot be null");
}
@Test
public void methodMatcherWhenMethodHasNotPreFilterAnnotationThenNotMatches() throws Exception {
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
assertThat(methodMatcher.matches(NoPreFilterClass.class.getMethod("doSomething"), NoPreFilterClass.class))
.isFalse();
}
@Test
public void methodMatcherWhenMethodHasPreFilterAnnotationThenMatches() throws Exception {
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
MethodMatcher methodMatcher = advice.getPointcut().getMethodMatcher();
assertThat(methodMatcher.matches(TestClass.class.getMethod("doSomethingListFilterTargetMatch", List.class),
TestClass.class)).isTrue();
}
@Test
public void findFilterTargetWhenNameProvidedAndNotMatchThenException() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetNotMatch", new Class[] { List.class }, new Object[] { new ArrayList<>() });
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
assertThatIllegalArgumentException().isThrownBy(() -> advice.invoke(methodInvocation)).withMessage(
"Filter target was null, or no argument with name 'filterTargetNotMatch' found in method.");
}
@Test
public void findFilterTargetWhenNameProvidedAndMatchAndNullThenException() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { null });
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
assertThatIllegalArgumentException().isThrownBy(() -> advice.invoke(methodInvocation))
.withMessage("Filter target was null, or no argument with name 'list' found in method.");
}
@Test
public void findFilterTargetWhenNameProvidedAndMatchAndNotNullThenFiltersList() throws Throwable {
List<String> list = new ArrayList<>();
list.add("john");
list.add("bob");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetMatch", new Class[] { List.class }, new Object[] { list });
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
advice.invoke(methodInvocation);
assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo("john");
}
@Test
public void findFilterTargetWhenNameNotProvidedAndSingleArgListNullThenException() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { null });
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
assertThatIllegalArgumentException().isThrownBy(() -> advice.invoke(methodInvocation))
.withMessage("Filter target was null. Make sure you passing the correct value in the method argument.");
}
@Test
public void findFilterTargetWhenNameNotProvidedAndSingleArgListThenFiltersList() throws Throwable {
List<String> list = new ArrayList<>();
list.add("john");
list.add("bob");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingListFilterTargetNotProvided", new Class[] { List.class }, new Object[] { list });
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
advice.invoke(methodInvocation);
assertThat(list).hasSize(1);
assertThat(list.get(0)).isEqualTo("john");
}
@Test
public void findFilterTargetWhenNameNotProvidedAndSingleArgArrayThenException() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingArrayFilterTargetNotProvided", new Class[] { String[].class },
new Object[] { new String[] {} });
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
assertThatIllegalStateException().isThrownBy(() -> advice.invoke(methodInvocation)).withMessage(
"Pre-filtering on array types is not supported. Using a Collection will solve this problem.");
}
@Test
public void findFilterTargetWhenNameNotProvidedAndNotSingleArgThenException() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomethingTwoArgsFilterTargetNotProvided", new Class[] { String.class, List.class },
new Object[] { "", new ArrayList<>() });
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
assertThatIllegalStateException().isThrownBy(() -> advice.invoke(methodInvocation))
.withMessage("Unable to determine the method argument for filtering. Specify the filter target.");
}
@Test
public void checkInheritedAnnotationsWhenDuplicatedThenAnnotationConfigurationException() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"inheritedAnnotations");
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> advice.invoke(methodInvocation));
}
@Test
public void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ConflictingAnnotations(),
ConflictingAnnotations.class, "inheritedAnnotations");
PreFilterAuthorizationMethodInterceptor advice = new PreFilterAuthorizationMethodInterceptor();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> advice.invoke(methodInvocation));
}
@PreFilter("filterObject == 'john'")
public static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {
@PreFilter(value = "filterObject == 'john'", filterTarget = "filterTargetNotMatch")
public List<String> doSomethingListFilterTargetNotMatch(List<String> list) {
return list;
}
@PreFilter(value = "filterObject == 'john'", filterTarget = "list")
public List<String> doSomethingListFilterTargetMatch(List<String> list) {
return list;
}
@PreFilter("filterObject == 'john'")
public List<String> doSomethingListFilterTargetNotProvided(List<String> list) {
return list;
}
@PreFilter("filterObject == 'john'")
public String[] doSomethingArrayFilterTargetNotProvided(String[] array) {
return array;
}
public List<String> doSomethingTwoArgsFilterTargetNotProvided(String s, List<String> list) {
return list;
}
@Override
public void inheritedAnnotations() {
}
}
public static class NoPreFilterClass {
public void doSomething() {
}
}
public static class ConflictingAnnotations implements InterfaceAnnotationsThree {
@Override
@PreFilter("filterObject == 'jack'")
public void inheritedAnnotations() {
}
}
public interface InterfaceAnnotationsOne {
@PreFilter("filterObject == 'jim'")
void inheritedAnnotations();
}
public interface InterfaceAnnotationsTwo {
@PreFilter("filterObject == 'jane'")
void inheritedAnnotations();
}
public interface InterfaceAnnotationsThree {
@MyPreFilter
void inheritedAnnotations();
}
@Retention(RetentionPolicy.RUNTIME)
@PreFilter("filterObject == 'john'")
public @interface MyPreFilter {
}
}

View File

@ -0,0 +1,195 @@
/*
* Copyright 2002-2021 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.
*/
package org.springframework.security.authorization.method;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.function.Supplier;
import org.junit.Test;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.core.Authentication;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for {@link SecuredAuthorizationManager}.
*
* @author Evgeniy Cheban
*/
public class SecuredAuthorizationManagerTests {
@Test
public void checkDoSomethingWhenNoSecuredAnnotationThenNullDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"doSomething");
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNull();
}
@Test
public void checkSecuredUserOrAdminWhenRoleUserThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"securedUserOrAdmin");
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkSecuredUserOrAdminWhenRoleAdminThenGrantedDecision() throws Exception {
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"securedUserOrAdmin");
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkSecuredUserOrAdminWhenRoleAnonymousThenDeniedDecision() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
"ROLE_ANONYMOUS");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"securedUserOrAdmin");
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, methodInvocation);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}
@Test
public void checkRequiresAdminWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),
ClassLevelAnnotations.class, "securedAdmin");
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, methodInvocation);
assertThat(decision.isGranted()).isFalse();
authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN");
decision = manager.check(authentication, methodInvocation);
assertThat(decision.isGranted()).isTrue();
}
@Test
public void checkRequiresUserWhenClassAnnotationsThenApplies() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),
ClassLevelAnnotations.class, "securedUser");
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
AuthorizationDecision decision = manager.check(authentication, methodInvocation);
assertThat(decision.isGranted()).isTrue();
authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN");
decision = manager.check(authentication, methodInvocation);
assertThat(decision.isGranted()).isFalse();
}
@Test
public void checkInheritedAnnotationsWhenDuplicatedThenAnnotationConfigurationException() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
"inheritedAnnotations");
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> manager.check(authentication, methodInvocation));
}
@Test
public void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationException() throws Exception {
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(),
ClassLevelAnnotations.class, "inheritedAnnotations");
SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
assertThatExceptionOfType(AnnotationConfigurationException.class)
.isThrownBy(() -> manager.check(authentication, methodInvocation));
}
public static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {
public void doSomething() {
}
@Secured({ "ROLE_USER", "ROLE_ADMIN" })
public void securedUserOrAdmin() {
}
@Override
public void inheritedAnnotations() {
}
}
@Secured("ROLE_USER")
public static class ClassLevelAnnotations implements InterfaceAnnotationsThree {
@Secured("ROLE_ADMIN")
public void securedAdmin() {
}
public void securedUser() {
}
@Override
@Secured("ROLE_ADMIN")
public void inheritedAnnotations() {
}
}
public interface InterfaceAnnotationsOne {
@Secured("ROLE_ADMIN")
void inheritedAnnotations();
}
public interface InterfaceAnnotationsTwo {
@Secured("ROLE_USER")
void inheritedAnnotations();
}
public interface InterfaceAnnotationsThree {
@MySecured
void inheritedAnnotations();
}
@Retention(RetentionPolicy.RUNTIME)
@Secured("ROLE_USER")
public @interface MySecured {
}
}

View File

@ -1,51 +1,11 @@
[[new]]
== What's New in Spring Security 5.5
== What's New in Spring Security 5.6
Spring Security 5.5 provides a number of new features.
Spring Security 5.6 provides a number of new features.
Below are the highlights of the release.
[[whats-new-servlet]]
=== Servlet
* OAuth 2.0 Client
** Added support for https://github.com/spring-projects/spring-security/pull/9520[Jwt Client Authentication] `private_key_jwt` and `client_secret_jwt`
** Added https://github.com/spring-projects/spring-security/pull/9535[Jwt Bearer Authorization Grant] support
** Added https://github.com/spring-projects/spring-security/pull/8765[R2DBC implementation] of `ReactiveOAuth2AuthorizedClientService`
* OAuth 2.0 Resource Server
** Enhanced JWT decoders https://github.com/spring-projects/spring-security/issues/7160[derivation of signature algorithms]
** Improved https://github.com/spring-projects/spring-security/issues/9100[content negotation]
** Improved https://github.com/spring-projects/spring-security/issues/9186[multi-tenancy support]
* SAML 2.0 Service Provider
** Added https://github.com/spring-projects/spring-security/issues/9095[OpenSAML 4 support]
** Enhanced SAML 2.0 https://github.com/spring-projects/spring-security/issues/9131[Assertion] https://github.com/spring-projects/spring-security/issues/9044[decryption]
** Added https://github.com/spring-projects/spring-security/issues/9028[file-based configuration] for asserting party metadata
** Improved https://github.com/spring-projects/spring-security/issues/9486[`RelyingPartyRegistration` resolution] support
** Enhanced relying party metadata https://github.com/spring-projects/spring-security/issues/9317[support]
** Added support for https://github.com/spring-projects/spring-security/issues/9177[AP-specified signing methods]
* Configuration
** Introduced https://github.com/spring-projects/spring-security/issues/9205[DispatcherType request matcher]
** Introduced https://github.com/spring-projects/spring-security/issues/8900[`AuthorizationManager`] for filter security
* Kotlin DSL
** Added https://github.com/spring-projects/spring-security/issues/9319[rememberMe support]
[[whats-new-webflux]]
=== WebFlux
** Added https://github.com/spring-projects/spring-security/issues/8143[Kotlin coroutine support] for `EnableReactiveMethodSecurity`
[[whats-new-build]]
=== Build
The build has been modernized in a number of different ways.
Here are a couple of highlights:
* All sample applications have been moved to https://github.com/spring-projects/spring-security-samples[a separate project]
* The full build now https://github.com/spring-projects/spring-security/issues/9419[works with JDK 11]
** Introduced https://github.com/spring-projects/spring-security/pull/9630[`AuthorizationManager`] for method security

View File

@ -759,6 +759,7 @@ A default implementation (with no ACL support) will be used if not supplied.
* <<nsa-global-method-security,global-method-security>>
* <<nsa-http,http>>
* <<nsa-method-security,method-security>>
* <<nsa-websocket-message-broker,websocket-message-broker>>
@ -2374,9 +2375,41 @@ If omitted, the namespace will generate a random value, preventing its accidenta
Cannot be empty.
[[nsa-method-security]]
=== Method Security
[[nsa-method-security]]
==== <method-security>
This element is the primary means of adding support for securing methods on Spring Security beans.
Methods can be secured by the use of annotations (defined at the interface or class level) or by defining a set of pointcuts.
[[nsa-method-security-attributes]]
===== <method-security> attributes
[[nsa-method-security-pre-post-enabled]]
* **pre-post-enabled**
Enables Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) for this application context.
Defaults to "true".
[[nsa-method-security-secured-enabled]]
* **secured-enabled**
Enables Spring Security's @Secured annotation for this application context.
Defaults to "false".
[[nsa-method-security-jsr250-enabled]]
* **jsr250-enabled**
Enables JSR-250 authorization annotations (@RolesAllowed, @PermitAll, @DenyAll) for this application context.
Defaults to "false".
[[nsa-method-security-proxy-target-class]]
* **proxy-target-class**
If true, class based proxying will be used instead of interface based proxying.
Defaults to "false".
[[nsa-method-security-children]]
===== Child Elements of <method-security>
* <<nsa-expression-handler,expression-handler>>
[[nsa-global-method-security]]
==== <global-method-security>

View File

@ -6,6 +6,598 @@ It provides support for JSR-250 annotation security as well as the framework's o
From 3.0 you can also make use of new <<el-access,expression-based annotations>>.
You can apply security to a single bean, using the `intercept-methods` element to decorate the bean declaration, or you can secure multiple beans across the entire service layer using the AspectJ style pointcuts.
=== EnableMethodSecurity
In Spring Security 5.6, we can enable annotation-based security using the `@EnableMethodSecurity` annotation on any `@Configuration` instance.
This improves upon `@EnableGlobalMethodSecurity` in a number of ways. `@EnableMethodSecurity`:
1. Uses the simplified `AuthorizationManager` API instead of metadata sources, config attributes, decision managers, and voters.
This simplifies reuse and customization.
2. Favors direct bean-based configuration, instead of requiring extending `GlobalMethodSecurityConfiguration` to customize beans
3. Is built using native Spring AOP, removing abstractions and allowing you to use Spring AOP building blocks to customize
4. Checks for conflicting annotations to ensure an unambiguous security configuration
5. Complies with JSR-250
6. Enables `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter` by default
[NOTE]
====
For earlier versions, please read about similar support with <<jc-enable-global-method-security, @EnableGlobalMethodSecurity>>.
====
For example, the following would enable Spring Security's `@PreAuthorize` annotation:
.Method Security Configuration
====
.Java
[source,java,role="primary"]
----
@EnableMethodSecurity
public class MethodSecurityConfig {
// ...
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableMethodSecurity
class MethodSecurityConfig {
// ...
}
----
.Xml
[source,xml,role="secondary"]
----
<sec:method-security/>
----
====
Adding an annotation to a method (on a class or interface) would then limit the access to that method accordingly.
Spring Security's native annotation support defines a set of attributes for the method.
These will be passed to the `DefaultAuthorizationMethodInterceptorChain` for it to make the actual decision:
.Method Security Annotation Usage
====
.Java
[source,java,role="primary"]
----
public interface BankService {
@PreAuthorize("hasRole('USER')")
Account readAccount(Long id);
@PreAuthorize("hasRole('USER')")
List<Account> findAccounts();
@PreAuthorize("hasRole('TELLER')")
Account post(Account account, Double amount);
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
interface BankService {
@PreAuthorize("hasRole('USER')")
fun readAccount(id : Long) : Account
@PreAuthorize("hasRole('USER')")
fun findAccounts() : List<Account>
@PreAuthorize("hasRole('TELLER')")
fun post(account : Account, amount : Double) : Account
}
----
====
You can enable support for Spring Security's `@Secured` annotation using:
.@Secured Configuration
====
.Java
[source,java,role="primary"]
----
@EnableMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig {
// ...
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableMethodSecurity(securedEnabled = true)
class MethodSecurityConfig {
// ...
}
----
.Xml
[source,xml,role="secondary"]
----
<sec:method-security secured-enabled="true"/>
----
====
or JSR-250 using:
.JSR-250 Configuration
====
.Java
[source,java,role="primary"]
----
@EnableMethodSecurity(jsr250Enabled = true)
public class MethodSecurityConfig {
// ...
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableMethodSecurity(jsr250Enabled = true)
class MethodSecurityConfig {
// ...
}
----
.Xml
[source,xml,role="secondary"]
----
<sec:method-security jsr250-enabled="true"/>
----
====
==== Customizing Authorization
Spring Security's `@PreAuthorize`, `@PostAuthorize`, `@PreFilter`, and `@PostFilter` ship with rich expression-based support.
[[jc-method-security-custom-expression-handler]]
If you need to customize the way that expressions are handled, you can expose a custom `MethodSecurityExpressionHandler`, like so:
.Custom MethodSecurityExpressionHandler
====
.Java
[source,java,role="primary"]
----
@Bean
static MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setTrustResolver(myCustomTrustResolver);
return handler;
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
companion object {
@Bean
fun methodSecurityExpressionHandler() : MethodSecurityExpressionHandler {
val handler = DefaultMethodSecurityExpressionHandler();
handler.setTrustResolver(myCustomTrustResolver);
return handler;
}
}
----
.Xml
[source,xml,role="secondary"]
----
<sec:method-security>
<sec:expression-handler ref="myExpressionHandler"/>
</sec:method-security>
<bean id="myExpressionHandler"
class="org.springframework.security.messaging.access.expression.DefaultMessageSecurityExpressionHandler">
<property name="trustResolver" ref="myCustomTrustResolver"/>
</bean>
----
====
[TIP]
====
We expose `MethodSecurityExpressionHandler` using a `static` method to ensure that Spring publishes it before it initializes Spring Security's method security `@Configuration` classes
====
Also, for role-based authorization, Spring Security adds a default `ROLE_` prefix, which is uses when evaluating expressions like `hasRole`.
[[jc-method-security-custom-granted-authority-defaults]]
You can configure the authorization rules to use a different prefix by exposing a `GrantedAuthorityDefaults` bean, like so:
.Custom MethodSecurityExpressionHandler
====
.Java
[source,java,role="primary"]
----
@Bean
static GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("MYPREFIX_");
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
companion object {
@Bean
fun grantedAuthorityDefaults() : GrantedAuthorityDefaults {
return GrantedAuthorityDefaults("MYPREFIX_");
}
}
----
.Xml
[source,xml,role="secondary"]
----
<sec:method-security/>
<bean id="grantedAuthorityDefaults" class="org.springframework.security.config.core.GrantedAuthorityDefaults">
<constructor-arg value="MYPREFIX_"/>
</bean>
----
====
[TIP]
====
We expose `GrantedAuthorityDefaults` using a `static` method to ensure that Spring publishes it before it initializes Spring Security's method security `@Configuration` classes
====
[[jc-method-security-custom-authorization-manager]]
==== Custom Authorization Managers
Method authorization is a combination of before- and after-method authorization.
[NOTE]
====
Before-method authorization is performed before the method is invoked.
If that authorization denies access, the method is not invoked, and an `AccessDeniedException` is thrown
After-method authorization is performed after the method is invoked, but before the method returns to the caller.
If that authorization denies access, the value is not returned, and an `AccessDeniedException` is thrown
====
To recreate what adding `@EnableMethodSecurity` does by default, you would publish the following configuration:
.Full Pre-post Method Security Configuration
====
.Java
[source,java,role="primary"]
----
@EnableMethodSecurity(prePostEnabled = false)
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor preFilterAuthorizationMethodInterceptor() {
return new PreFilterAuthorizationMethodInterceptor();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor preAuthorizeAuthorizationMethodInterceptor() {
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor postAuthorizeAuthorizationMethodInterceptor() {
return AuthorizationManagerAfterMethodInterceptor.postAuthorize();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor postFilterAuthorizationMethodInterceptor() {
return new PostFilterAuthorizationMethodInterceptor();
}
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableMethodSecurity(prePostEnabled = false)
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun preFilterAuthorizationMethodInterceptor() : Advisor {
return PreFilterAuthorizationMethodInterceptor();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun preAuthorizeAuthorizationMethodInterceptor() : Advisor {
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun postAuthorizeAuthorizationMethodInterceptor() : Advisor {
return AuthorizationManagerAfterMethodInterceptor.postAuthorize();
}
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun postFilterAuthorizationMethodInterceptor() : Advisor {
return PostFilterAuthorizationMethodInterceptor();
}
}
----
.Xml
[source,xml,role="secondary"]
----
<sec:method-security pre-post-enabled="false"/>
<aop:config/>
<bean id="preFilterAuthorizationMethodInterceptor"
class="org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor"/>
<bean id="preAuthorizeAuthorizationMethodInterceptor"
class="org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor"
factory-method="preAuthorize"/>
<bean id="postAuthorizeAuthorizationMethodInterceptor"
class="org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor"
factory-method="postAuthorize"/>
<bean id="postFilterAuthorizationMethodInterceptor"
class="org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor"/>
----
====
Notice that Spring Security's method security is built using Spring AOP.
So, interceptors are invoked based on the order specified.
This can be customized by calling `setOrder` on the interceptor instances like so:
.Publish Custom Advisor
====
.Java
[source,java,role="primary"]
----
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor postFilterAuthorizationMethodInterceptor() {
PostFilterAuthorizationMethodInterceptor interceptor = new PostFilterAuthorizationMethodInterceptor();
interceptor.setOrder(AuthorizationInterceptorOrders.POST_AUTHORIZE.getOrder() - 1);
return interceptor;
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun postFilterAuthorizationMethodInterceptor() : Advisor {
val interceptor = PostFilterAuthorizationMethodInterceptor();
interceptor.setOrder(AuthorizationInterceptorOrders.POST_AUTHORIZE.getOrder() - 1);
return interceptor;
}
----
.Xml
[source,xml,role="secondary"]
----
<bean id="postFilterAuthorizationMethodInterceptor"
class="org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor">
<property name="order"
value="#{T(org.springframework.security.authorization.method.AuthorizationInterceptorsOrder).POST_AUTHORIZE.getOrder() -1}"/>
</bean>
----
====
You may want to only support `@PreAuthorize` in your application, in which case you can do the following:
.Only @PreAuthorize Configuration
====
.Java
[source,java,role="primary"]
----
@EnableMethodSecurity(prePostEnabled = false)
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
Advisor preAuthorize() {
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize();
}
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableMethodSecurity(prePostEnabled = false)
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun preAuthorize() : Advisor {
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize()
}
}
----
.Xml
[source,xml,role="secondary"]
----
<sec:method-security pre-post-enabled="false"/>
<aop:config/>
<bean id="preAuthorizeAuthorizationMethodInterceptor"
class="org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor"
factory-method="preAuthorize"/>
----
====
Or, you may have a custom before-method `AuthorizationManager` that you want to add to the list.
In this case, you will need to tell Spring Security both the `AuthorizationManager` and to which methods and classes your authorization manager applies.
Thus, you can configure Spring Security to invoke your `AuthorizationManager` in between `@PreAuthorize` and `@PostAuthorize` like so:
.Custom Before Advisor
====
.Java
[source,java,role="primary"]
----
@EnableMethodSecurity
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public Advisor customAuthorize() {
JdkRegexpMethodPointcut pattern = new JdkRegexpMethodPointcut();
pattern.setPattern("org.mycompany.myapp.service.*");
AuthorizationManager<MethodInvocation> rule = AuthorityAuthorizationManager.isAuthenticated();
AuthorizationManagerBeforeMethodInterceptor interceptor = new AuthorizationManagerBeforeMethodInterceptor(pattern, rule);
interceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1);
return interceptor;
}
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableMethodSecurity
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun customAuthorize() : Advisor {
val pattern = JdkRegexpMethodPointcut();
pattern.setPattern("org.mycompany.myapp.service.*");
val rule = AuthorityAuthorizationManager.isAuthenticated();
val interceptor = AuthorizationManagerBeforeMethodInterceptor(pattern, rule);
interceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1);
return interceptor;
}
}
----
.Xml
[source,xml,role="secondary"]
----
<sec:method-security/>
<aop:config/>
<bean id="customAuthorize"
class="org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor">
<constructor-arg>
<bean class="org.springframework.aop.support.JdkRegexpMethodPointcut">
<property name="pattern" value="org.mycompany.myapp.service.*"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.authorization.AuthorityAuthorizationManager"
factory-method="isAuthenticated"/>
</constructor-arg>
<property name="order"
value="#{T(org.springframework.security.authorization.method.AuthorizationInterceptorsOrder).PRE_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1}"/>
</bean>
----
====
[TIP]
====
You can place your interceptor in between Spring Security method interceptors using the order constants specified in `AuthorizationInterceptorsOrder`.
====
The same can be done for after-method authorization.
After-method authorization is generally concerned with analysing the return value to verify access.
For example, you might have a method that confirms that the account requested actually belongs to the logged-in user like so:
.@PostAuthorize example
====
.Java
[source,java,role="primary"]
----
public interface BankService {
@PreAuthorize("hasRole('USER')")
@PostAuthorize("returnObject.owner == authentication.name")
Account readAccount(Long id);
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
interface BankService {
@PreAuthorize("hasRole('USER')")
@PostAuthorize("returnObject.owner == authentication.name")
fun readAccount(id : Long) : Account
}
----
====
You can supply your own `AuthorizationMethodInterceptor` to customize how access to the return value is evaluated.
For example, if you have your own custom annotation, you can configure it like so:
.Custom After Advisor
====
.Java
[source,java,role="primary"]
----
@EnableMethodSecurity
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public Advisor customAuthorize(AuthorizationManager<MethodInvocationResult> rules) {
AnnotationMethodMatcher pattern = new AnnotationMethodMatcher(MySecurityAnnotation.class);
AuthorizationManagerAfterMethodInterceptor interceptor = new AuthorizationManagerAfterMethodInterceptor(pattern, rules);
interceptor.setOrder(AuthorizationInterceptorsOrder.POST_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1);
return interceptor;
}
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@EnableMethodSecurity
class MethodSecurityConfig {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
fun customAuthorize(rules : AuthorizationManager<MethodInvocationResult>) : Advisor {
val pattern = AnnotationMethodMatcher(MySecurityAnnotation::class.java);
val interceptor = AuthorizationManagerAfterMethodInterceptor(pattern, rules);
interceptor.setOrder(AuthorizationInterceptorsOrder.POST_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1);
return interceptor;
}
}
----
.Xml
[source,xml,role="secondary"]
----
<sec:method-security/>
<aop:config/>
<bean id="customAuthorize"
class="org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor">
<constructor-arg>
<bean class="org.springframework.aop.support.annotation.AnnotationMethodMatcher">
<constructor-arg value="#{T(org.mycompany.MySecurityAnnotation)}"/>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.authorization.AuthorityAuthorizationManager"
factory-method="isAuthenticated"/>
</constructor-arg>
<property name="order"
value="#{T(org.springframework.security.authorization.method.AuthorizationInterceptorsOrder).PRE_AUTHORIZE_ADVISOR_ORDER.getOrder() + 1}"/>
</bean>
----
====
and it will be invoked after the `@PostAuthorize` interceptor.
[[jc-enable-global-method-security]]
=== EnableGlobalMethodSecurity
We can enable annotation-based security using the `@EnableGlobalMethodSecurity` annotation on any `@Configuration` instance.