Skip to content

Commit 2b7e042

Browse files
committed
Consider AuthorizationManager for Method Security
Closes gh-9289
1 parent 76229cf commit 2b7e042

21 files changed

+2746
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright 2002-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.method.configuration;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
import org.springframework.context.annotation.AdviceMode;
26+
import org.springframework.context.annotation.Configuration;
27+
import org.springframework.context.annotation.Import;
28+
import org.springframework.core.Ordered;
29+
30+
/**
31+
* Enables Spring Security Method Security.
32+
* @author Evgeniy Cheban
33+
*/
34+
@Retention(RetentionPolicy.RUNTIME)
35+
@Target(ElementType.TYPE)
36+
@Documented
37+
@Import(MethodSecuritySelector.class)
38+
@Configuration
39+
public @interface EnableMethodSecurity {
40+
41+
/**
42+
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to
43+
* standard Java interface-based proxies. The default is {@code false}. <strong>
44+
* Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>.
45+
* <p>
46+
* Note that setting this attribute to {@code true} will affect <em>all</em>
47+
* Spring-managed beans requiring proxying, not just those marked with
48+
* {@code @Cacheable}. For example, other beans marked with Spring's
49+
* {@code @Transactional} annotation will be upgraded to subclass proxying at the same
50+
* time. This approach has no negative impact in practice unless one is explicitly
51+
* expecting one type of proxy vs another, e.g. in tests.
52+
* @return true if subclass-based (CGLIB) proxies are to be created
53+
*/
54+
boolean proxyTargetClass() default false;
55+
56+
/**
57+
* Indicate how security advice should be applied. The default is
58+
* {@link AdviceMode#PROXY}.
59+
* @see AdviceMode
60+
* @return the {@link AdviceMode} to use
61+
*/
62+
AdviceMode mode() default AdviceMode.PROXY;
63+
64+
/**
65+
* Indicate the ordering of the execution of the security advisor when multiple
66+
* advices are applied at a specific joinpoint. The default is
67+
* {@link Ordered#LOWEST_PRECEDENCE}.
68+
* @return the order the security advisor should be applied
69+
*/
70+
int order() default Ordered.LOWEST_PRECEDENCE;
71+
72+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Copyright 2002-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.method.configuration;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
import org.aopalliance.intercept.MethodInvocation;
23+
24+
import org.springframework.beans.factory.annotation.Autowired;
25+
import org.springframework.beans.factory.config.BeanDefinition;
26+
import org.springframework.context.annotation.Bean;
27+
import org.springframework.context.annotation.Configuration;
28+
import org.springframework.context.annotation.ImportAware;
29+
import org.springframework.context.annotation.Role;
30+
import org.springframework.core.type.AnnotationMetadata;
31+
import org.springframework.security.access.annotation.SecuredAnnotationAuthorizationManagerBeforeAdvice;
32+
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
33+
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
34+
import org.springframework.security.access.expression.method.PostAnnotationAuthorizationManagerAfterAdvice;
35+
import org.springframework.security.access.expression.method.PreAnnotationAuthorizationManagerBeforeAdvice;
36+
import org.springframework.security.access.intercept.aopalliance.AuthorizationMethodInterceptor;
37+
import org.springframework.security.access.intercept.aopalliance.MethodSecurityAuthorizationManagerAdvisor;
38+
import org.springframework.security.access.method.AuthorizationManagerAfterAdvice;
39+
import org.springframework.security.access.method.AuthorizationManagerBeforeAdvice;
40+
import org.springframework.security.access.method.DelegatingAuthorizationManagerAfterAdvice;
41+
import org.springframework.security.access.method.DelegatingAuthorizationManagerBeforeAdvice;
42+
import org.springframework.security.access.method.MethodAuthorizationContext;
43+
44+
/**
45+
* Base {@link Configuration} for enabling Spring Security Method Security.
46+
*
47+
* @author Evgeniy Cheban
48+
* @see EnableMethodSecurity
49+
*/
50+
@Configuration(proxyBeanMethods = false)
51+
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
52+
final class MethodSecurityConfiguration implements ImportAware {
53+
54+
private MethodSecurityExpressionHandler methodSecurityExpressionHandler;
55+
56+
private AuthorizationManagerBeforeAdvice<MethodInvocation> authorizationManagerBeforeAdvice;
57+
58+
private AuthorizationManagerAfterAdvice<MethodInvocation> authorizationManagerAfterAdvice;
59+
60+
private int advisorOrder;
61+
62+
@Bean
63+
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
64+
MethodSecurityAuthorizationManagerAdvisor methodSecurityAdvisor(AuthorizationMethodInterceptor interceptor) {
65+
MethodSecurityAuthorizationManagerAdvisor advisor = new MethodSecurityAuthorizationManagerAdvisor(interceptor,
66+
getAuthorizationManagerBeforeAdvice(), getAuthorizationManagerAfterAdvice());
67+
advisor.setOrder(this.advisorOrder);
68+
return advisor;
69+
}
70+
71+
@Bean
72+
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
73+
AuthorizationMethodInterceptor authorizationMethodInterceptor() {
74+
return new AuthorizationMethodInterceptor(getAuthorizationManagerBeforeAdvice(),
75+
getAuthorizationManagerAfterAdvice());
76+
}
77+
78+
private MethodSecurityExpressionHandler getMethodSecurityExpressionHandler() {
79+
if (this.methodSecurityExpressionHandler == null) {
80+
this.methodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
81+
}
82+
return this.methodSecurityExpressionHandler;
83+
}
84+
85+
@Autowired(required = false)
86+
void setMethodSecurityExpressionHandler(MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
87+
this.methodSecurityExpressionHandler = methodSecurityExpressionHandler;
88+
}
89+
90+
private AuthorizationManagerBeforeAdvice<MethodInvocation> getAuthorizationManagerBeforeAdvice() {
91+
if (this.authorizationManagerBeforeAdvice == null) {
92+
this.authorizationManagerBeforeAdvice = createDefaultAuthorizationManagerBeforeAdvice();
93+
}
94+
return this.authorizationManagerBeforeAdvice;
95+
}
96+
97+
private AuthorizationManagerBeforeAdvice<MethodInvocation> createDefaultAuthorizationManagerBeforeAdvice() {
98+
List<AuthorizationManagerBeforeAdvice<MethodAuthorizationContext>> beforeAdvices = new ArrayList<>();
99+
beforeAdvices.add(getPreAnnotationAuthorizationManagerBeforeAdvice());
100+
beforeAdvices.add(new SecuredAnnotationAuthorizationManagerBeforeAdvice());
101+
return new DelegatingAuthorizationManagerBeforeAdvice(beforeAdvices);
102+
}
103+
104+
private PreAnnotationAuthorizationManagerBeforeAdvice getPreAnnotationAuthorizationManagerBeforeAdvice() {
105+
PreAnnotationAuthorizationManagerBeforeAdvice preAnnotationBeforeAdvice = new PreAnnotationAuthorizationManagerBeforeAdvice();
106+
preAnnotationBeforeAdvice.setExpressionHandler(getMethodSecurityExpressionHandler());
107+
return preAnnotationBeforeAdvice;
108+
}
109+
110+
@Autowired(required = false)
111+
void setAuthorizationManagerBeforeAdvice(
112+
AuthorizationManagerBeforeAdvice<MethodInvocation> authorizationManagerBeforeAdvice) {
113+
this.authorizationManagerBeforeAdvice = authorizationManagerBeforeAdvice;
114+
}
115+
116+
private AuthorizationManagerAfterAdvice<MethodInvocation> getAuthorizationManagerAfterAdvice() {
117+
if (this.authorizationManagerAfterAdvice == null) {
118+
this.authorizationManagerAfterAdvice = createDefaultAuthorizationManagerAfterAdvice();
119+
}
120+
return this.authorizationManagerAfterAdvice;
121+
}
122+
123+
private AuthorizationManagerAfterAdvice<MethodInvocation> createDefaultAuthorizationManagerAfterAdvice() {
124+
List<AuthorizationManagerAfterAdvice<MethodAuthorizationContext>> afterAdvices = new ArrayList<>();
125+
afterAdvices.add(getPostAnnotationAuthorizationManagerAfterAdvice());
126+
return new DelegatingAuthorizationManagerAfterAdvice(afterAdvices);
127+
}
128+
129+
private PostAnnotationAuthorizationManagerAfterAdvice getPostAnnotationAuthorizationManagerAfterAdvice() {
130+
PostAnnotationAuthorizationManagerAfterAdvice postAnnotationAfterAdvice = new PostAnnotationAuthorizationManagerAfterAdvice();
131+
postAnnotationAfterAdvice.setExpressionHandler(getMethodSecurityExpressionHandler());
132+
return postAnnotationAfterAdvice;
133+
}
134+
135+
@Autowired(required = false)
136+
void setAuthorizationManagerAfterAdvice(
137+
AuthorizationManagerAfterAdvice<MethodInvocation> authorizationManagerAfterAdvice) {
138+
this.authorizationManagerAfterAdvice = authorizationManagerAfterAdvice;
139+
}
140+
141+
@Override
142+
public void setImportMetadata(AnnotationMetadata importMetadata) {
143+
this.advisorOrder = (int) importMetadata.getAnnotationAttributes(EnableMethodSecurity.class.getName())
144+
.get("order");
145+
}
146+
147+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2002-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.method.configuration;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
import org.springframework.context.annotation.AdviceMode;
23+
import org.springframework.context.annotation.AdviceModeImportSelector;
24+
import org.springframework.context.annotation.AutoProxyRegistrar;
25+
26+
/**
27+
* Dynamically determines which imports to include using the {@link EnableMethodSecurity}
28+
* annotation.
29+
*
30+
* @author Evgeniy Cheban
31+
*/
32+
final class MethodSecuritySelector extends AdviceModeImportSelector<EnableMethodSecurity> {
33+
34+
@Override
35+
protected String[] selectImports(AdviceMode adviceMode) {
36+
if (adviceMode == AdviceMode.PROXY) {
37+
return getProxyImports();
38+
}
39+
throw new IllegalStateException("AdviceMode '" + adviceMode + "' is not supported");
40+
}
41+
42+
private String[] getProxyImports() {
43+
List<String> result = new ArrayList<>();
44+
result.add(AutoProxyRegistrar.class.getName());
45+
result.add(MethodSecurityConfiguration.class.getName());
46+
return result.toArray(new String[0]);
47+
}
48+
49+
}

0 commit comments

Comments
 (0)