Skip to content

Commit

Permalink
Consider AuthorizationManager for Method Security
Browse files Browse the repository at this point in the history
Closes gh-9289
  • Loading branch information
evgeniycheban committed Feb 11, 2021
1 parent 76229cf commit 2b7e042
Show file tree
Hide file tree
Showing 21 changed files with 2,746 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
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;

/**
* Enables Spring Security Method Security.
* @author Evgeniy Cheban
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MethodSecuritySelector.class)
@Configuration
public @interface EnableMethodSecurity {

/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to
* standard Java interface-based proxies. The default is {@code false}. <strong>
* Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>.
* <p>
* Note that setting this attribute to {@code true} will affect <em>all</em>
* Spring-managed beans requiring proxying, not just those marked with
* {@code @Cacheable}. For example, other beans marked with Spring's
* {@code @Transactional} annotation will be upgraded to subclass proxying at the same
* time. This approach has no negative impact in practice unless one is explicitly
* expecting one type of proxy vs another, e.g. in tests.
* @return true if subclass-based (CGLIB) proxies are to be created
*/
boolean proxyTargetClass() default false;

/**
* Indicate how security advice should be applied. The default is
* {@link AdviceMode#PROXY}.
* @see AdviceMode
* @return the {@link AdviceMode} to use
*/
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;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* 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.util.ArrayList;
import java.util.List;

import org.aopalliance.intercept.MethodInvocation;

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.type.AnnotationMetadata;
import org.springframework.security.access.annotation.SecuredAnnotationAuthorizationManagerBeforeAdvice;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.PostAnnotationAuthorizationManagerAfterAdvice;
import org.springframework.security.access.expression.method.PreAnnotationAuthorizationManagerBeforeAdvice;
import org.springframework.security.access.intercept.aopalliance.AuthorizationMethodInterceptor;
import org.springframework.security.access.intercept.aopalliance.MethodSecurityAuthorizationManagerAdvisor;
import org.springframework.security.access.method.AuthorizationManagerAfterAdvice;
import org.springframework.security.access.method.AuthorizationManagerBeforeAdvice;
import org.springframework.security.access.method.DelegatingAuthorizationManagerAfterAdvice;
import org.springframework.security.access.method.DelegatingAuthorizationManagerBeforeAdvice;
import org.springframework.security.access.method.MethodAuthorizationContext;

/**
* Base {@link Configuration} for enabling Spring Security Method Security.
*
* @author Evgeniy Cheban
* @see EnableMethodSecurity
*/
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
final class MethodSecurityConfiguration implements ImportAware {

private MethodSecurityExpressionHandler methodSecurityExpressionHandler;

private AuthorizationManagerBeforeAdvice<MethodInvocation> authorizationManagerBeforeAdvice;

private AuthorizationManagerAfterAdvice<MethodInvocation> authorizationManagerAfterAdvice;

private int advisorOrder;

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
MethodSecurityAuthorizationManagerAdvisor methodSecurityAdvisor(AuthorizationMethodInterceptor interceptor) {
MethodSecurityAuthorizationManagerAdvisor advisor = new MethodSecurityAuthorizationManagerAdvisor(interceptor,
getAuthorizationManagerBeforeAdvice(), getAuthorizationManagerAfterAdvice());
advisor.setOrder(this.advisorOrder);
return advisor;
}

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
AuthorizationMethodInterceptor authorizationMethodInterceptor() {
return new AuthorizationMethodInterceptor(getAuthorizationManagerBeforeAdvice(),
getAuthorizationManagerAfterAdvice());
}

private MethodSecurityExpressionHandler getMethodSecurityExpressionHandler() {
if (this.methodSecurityExpressionHandler == null) {
this.methodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
}
return this.methodSecurityExpressionHandler;
}

@Autowired(required = false)
void setMethodSecurityExpressionHandler(MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
this.methodSecurityExpressionHandler = methodSecurityExpressionHandler;
}

private AuthorizationManagerBeforeAdvice<MethodInvocation> getAuthorizationManagerBeforeAdvice() {
if (this.authorizationManagerBeforeAdvice == null) {
this.authorizationManagerBeforeAdvice = createDefaultAuthorizationManagerBeforeAdvice();
}
return this.authorizationManagerBeforeAdvice;
}

private AuthorizationManagerBeforeAdvice<MethodInvocation> createDefaultAuthorizationManagerBeforeAdvice() {
List<AuthorizationManagerBeforeAdvice<MethodAuthorizationContext>> beforeAdvices = new ArrayList<>();
beforeAdvices.add(getPreAnnotationAuthorizationManagerBeforeAdvice());
beforeAdvices.add(new SecuredAnnotationAuthorizationManagerBeforeAdvice());
return new DelegatingAuthorizationManagerBeforeAdvice(beforeAdvices);
}

private PreAnnotationAuthorizationManagerBeforeAdvice getPreAnnotationAuthorizationManagerBeforeAdvice() {
PreAnnotationAuthorizationManagerBeforeAdvice preAnnotationBeforeAdvice = new PreAnnotationAuthorizationManagerBeforeAdvice();
preAnnotationBeforeAdvice.setExpressionHandler(getMethodSecurityExpressionHandler());
return preAnnotationBeforeAdvice;
}

@Autowired(required = false)
void setAuthorizationManagerBeforeAdvice(
AuthorizationManagerBeforeAdvice<MethodInvocation> authorizationManagerBeforeAdvice) {
this.authorizationManagerBeforeAdvice = authorizationManagerBeforeAdvice;
}

private AuthorizationManagerAfterAdvice<MethodInvocation> getAuthorizationManagerAfterAdvice() {
if (this.authorizationManagerAfterAdvice == null) {
this.authorizationManagerAfterAdvice = createDefaultAuthorizationManagerAfterAdvice();
}
return this.authorizationManagerAfterAdvice;
}

private AuthorizationManagerAfterAdvice<MethodInvocation> createDefaultAuthorizationManagerAfterAdvice() {
List<AuthorizationManagerAfterAdvice<MethodAuthorizationContext>> afterAdvices = new ArrayList<>();
afterAdvices.add(getPostAnnotationAuthorizationManagerAfterAdvice());
return new DelegatingAuthorizationManagerAfterAdvice(afterAdvices);
}

private PostAnnotationAuthorizationManagerAfterAdvice getPostAnnotationAuthorizationManagerAfterAdvice() {
PostAnnotationAuthorizationManagerAfterAdvice postAnnotationAfterAdvice = new PostAnnotationAuthorizationManagerAfterAdvice();
postAnnotationAfterAdvice.setExpressionHandler(getMethodSecurityExpressionHandler());
return postAnnotationAfterAdvice;
}

@Autowired(required = false)
void setAuthorizationManagerAfterAdvice(
AuthorizationManagerAfterAdvice<MethodInvocation> authorizationManagerAfterAdvice) {
this.authorizationManagerAfterAdvice = authorizationManagerAfterAdvice;
}

@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.advisorOrder = (int) importMetadata.getAnnotationAttributes(EnableMethodSecurity.class.getName())
.get("order");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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.util.ArrayList;
import java.util.List;

import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AdviceModeImportSelector;
import org.springframework.context.annotation.AutoProxyRegistrar;

/**
* Dynamically determines which imports to include using the {@link EnableMethodSecurity}
* annotation.
*
* @author Evgeniy Cheban
*/
final class MethodSecuritySelector extends AdviceModeImportSelector<EnableMethodSecurity> {

@Override
protected String[] selectImports(AdviceMode adviceMode) {
if (adviceMode == AdviceMode.PROXY) {
return getProxyImports();
}
throw new IllegalStateException("AdviceMode '" + adviceMode + "' is not supported");
}

private String[] getProxyImports() {
List<String> result = new ArrayList<>();
result.add(AutoProxyRegistrar.class.getName());
result.add(MethodSecurityConfiguration.class.getName());
return result.toArray(new String[0]);
}

}
Loading

0 comments on commit 2b7e042

Please sign in to comment.