Skip to content

Commit 74a3365

Browse files
jzheauxAyush Kohli
authored and
Ayush Kohli
committed
Polish AOP Structure
- Changed from MethodMatcher to Pointcut since authorization annotations also can be attached to classes - Adjusted advice to extend Before or AfterAdvice - Adjusted advice to extend PointcutAdvisor so that it can share its Pointcut - Adjusted advice to extend AopInfrastructureBean to align with old advice classes Issue spring-projectsgh-9289
1 parent ff9c97f commit 74a3365

17 files changed

+266
-218
lines changed

config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityConfiguration.java

Lines changed: 26 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -17,40 +17,35 @@
1717
package org.springframework.security.config.annotation.method.configuration;
1818

1919
import java.lang.annotation.Annotation;
20-
import java.lang.reflect.AnnotatedElement;
21-
import java.lang.reflect.Method;
2220
import java.util.ArrayList;
23-
import java.util.Arrays;
24-
import java.util.HashSet;
2521
import java.util.List;
2622
import java.util.Map;
27-
import java.util.Set;
2823

2924
import javax.annotation.security.DenyAll;
3025
import javax.annotation.security.PermitAll;
3126
import javax.annotation.security.RolesAllowed;
3227

33-
import org.springframework.aop.MethodMatcher;
3428
import org.springframework.aop.Pointcut;
35-
import org.springframework.aop.support.AopUtils;
29+
import org.springframework.aop.support.ComposablePointcut;
3630
import org.springframework.aop.support.DefaultPointcutAdvisor;
3731
import org.springframework.aop.support.Pointcuts;
38-
import org.springframework.aop.support.StaticMethodMatcher;
32+
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
3933
import org.springframework.beans.factory.InitializingBean;
4034
import org.springframework.beans.factory.annotation.Autowired;
4135
import org.springframework.beans.factory.config.BeanDefinition;
4236
import org.springframework.context.annotation.Bean;
4337
import org.springframework.context.annotation.Configuration;
4438
import org.springframework.context.annotation.ImportAware;
4539
import org.springframework.context.annotation.Role;
46-
import org.springframework.core.annotation.AnnotatedElementUtils;
4740
import org.springframework.core.annotation.AnnotationAttributes;
4841
import org.springframework.core.type.AnnotationMetadata;
4942
import org.springframework.security.access.annotation.Secured;
5043
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
5144
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
5245
import org.springframework.security.access.prepost.PostAuthorize;
46+
import org.springframework.security.access.prepost.PostFilter;
5347
import org.springframework.security.access.prepost.PreAuthorize;
48+
import org.springframework.security.access.prepost.PreFilter;
5449
import org.springframework.security.authorization.method.AuthorizationManagerMethodAfterAdvice;
5550
import org.springframework.security.authorization.method.AuthorizationManagerMethodBeforeAdvice;
5651
import org.springframework.security.authorization.method.AuthorizationMethodAfterAdvice;
@@ -72,6 +67,7 @@
7267
* Base {@link Configuration} for enabling Spring Security Method Security.
7368
*
7469
* @author Evgeniy Cheban
70+
* @author Josh Cummings
7571
* @see EnableMethodSecurity
7672
* @since 5.5
7773
*/
@@ -92,7 +88,9 @@ final class MethodSecurityConfiguration implements ImportAware, InitializingBean
9288
@Bean
9389
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
9490
DefaultPointcutAdvisor methodSecurityAdvisor(AuthorizationMethodInterceptor interceptor) {
95-
Pointcut pointcut = Pointcuts.union(getAuthorizationMethodBeforeAdvice(), getAuthorizationMethodAfterAdvice());
91+
AuthorizationMethodBeforeAdvice<?> beforeAdvice = getAuthorizationMethodBeforeAdvice();
92+
AuthorizationMethodAfterAdvice<?> afterAdvice = getAuthorizationMethodAfterAdvice();
93+
Pointcut pointcut = Pointcuts.union(beforeAdvice.getPointcut(), afterAdvice.getPointcut());
9694
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, interceptor);
9795
advisor.setOrder(order());
9896
return advisor;
@@ -147,32 +145,34 @@ private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> createDefaul
147145
}
148146

149147
private PreFilterAuthorizationMethodBeforeAdvice getPreFilterAuthorizationMethodBeforeAdvice() {
150-
PreFilterAuthorizationMethodBeforeAdvice preFilterBeforeAdvice = new PreFilterAuthorizationMethodBeforeAdvice();
148+
Pointcut pointcut = forAnnotation(PreFilter.class);
149+
PreFilterAuthorizationMethodBeforeAdvice preFilterBeforeAdvice = new PreFilterAuthorizationMethodBeforeAdvice(
150+
pointcut);
151151
preFilterBeforeAdvice.setExpressionHandler(getMethodSecurityExpressionHandler());
152152
return preFilterBeforeAdvice;
153153
}
154154

155155
private AuthorizationMethodBeforeAdvice<MethodAuthorizationContext> getPreAuthorizeAuthorizationMethodBeforeAdvice() {
156-
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(PreAuthorize.class);
156+
Pointcut pointcut = forAnnotation(PreAuthorize.class);
157157
PreAuthorizeAuthorizationManager authorizationManager = new PreAuthorizeAuthorizationManager();
158158
authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler());
159-
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager);
159+
return new AuthorizationManagerMethodBeforeAdvice<>(pointcut, authorizationManager);
160160
}
161161

162162
private AuthorizationManagerMethodBeforeAdvice<MethodAuthorizationContext> getSecuredAuthorizationMethodBeforeAdvice() {
163-
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(Secured.class);
163+
Pointcut pointcut = forAnnotation(Secured.class);
164164
SecuredAuthorizationManager authorizationManager = new SecuredAuthorizationManager();
165-
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager);
165+
return new AuthorizationManagerMethodBeforeAdvice<>(pointcut, authorizationManager);
166166
}
167167

168168
private AuthorizationManagerMethodBeforeAdvice<MethodAuthorizationContext> getJsr250AuthorizationMethodBeforeAdvice() {
169-
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(DenyAll.class, PermitAll.class,
170-
RolesAllowed.class);
169+
Pointcut pointcut = new ComposablePointcut(forAnnotation(DenyAll.class)).union(forAnnotation(PermitAll.class))
170+
.union(forAnnotation(RolesAllowed.class));
171171
Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager();
172172
if (this.grantedAuthorityDefaults != null) {
173173
authorizationManager.setRolePrefix(this.grantedAuthorityDefaults.getRolePrefix());
174174
}
175-
return new AuthorizationManagerMethodBeforeAdvice<>(methodMatcher, authorizationManager);
175+
return new AuthorizationManagerMethodBeforeAdvice<>(pointcut, authorizationManager);
176176
}
177177

178178
@Autowired(required = false)
@@ -196,16 +196,18 @@ private AuthorizationMethodAfterAdvice<MethodAuthorizationContext> createDefault
196196
}
197197

198198
private PostFilterAuthorizationMethodAfterAdvice getPostFilterAuthorizationMethodAfterAdvice() {
199-
PostFilterAuthorizationMethodAfterAdvice postFilterAfterAdvice = new PostFilterAuthorizationMethodAfterAdvice();
199+
Pointcut pointcut = forAnnotation(PostFilter.class);
200+
PostFilterAuthorizationMethodAfterAdvice postFilterAfterAdvice = new PostFilterAuthorizationMethodAfterAdvice(
201+
pointcut);
200202
postFilterAfterAdvice.setExpressionHandler(getMethodSecurityExpressionHandler());
201203
return postFilterAfterAdvice;
202204
}
203205

204206
private AuthorizationManagerMethodAfterAdvice<MethodAuthorizationContext> getPostAuthorizeAuthorizationMethodAfterAdvice() {
205-
MethodMatcher methodMatcher = new SecurityAnnotationsStaticMethodMatcher(PostAuthorize.class);
207+
Pointcut pointcut = forAnnotation(PostAuthorize.class);
206208
PostAuthorizeAuthorizationManager authorizationManager = new PostAuthorizeAuthorizationManager();
207209
authorizationManager.setExpressionHandler(getMethodSecurityExpressionHandler());
208-
return new AuthorizationManagerMethodAfterAdvice<>(methodMatcher, authorizationManager);
210+
return new AuthorizationManagerMethodAfterAdvice<>(pointcut, authorizationManager);
209211
}
210212

211213
@Autowired(required = false)
@@ -241,27 +243,9 @@ private int order() {
241243
return this.enableMethodSecurity.getNumber("order");
242244
}
243245

244-
private static final class SecurityAnnotationsStaticMethodMatcher extends StaticMethodMatcher {
245-
246-
private final Set<Class<? extends Annotation>> annotationClasses;
247-
248-
@SafeVarargs
249-
private SecurityAnnotationsStaticMethodMatcher(Class<? extends Annotation>... annotationClasses) {
250-
this.annotationClasses = new HashSet<>(Arrays.asList(annotationClasses));
251-
}
252-
253-
@Override
254-
public boolean matches(Method method, Class<?> targetClass) {
255-
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
256-
return hasAnnotations(specificMethod) || hasAnnotations(specificMethod.getDeclaringClass());
257-
}
258-
259-
private boolean hasAnnotations(AnnotatedElement annotatedElement) {
260-
Set<Annotation> annotations = AnnotatedElementUtils.findAllMergedAnnotations(annotatedElement,
261-
this.annotationClasses);
262-
return !annotations.isEmpty();
263-
}
264-
246+
private Pointcut forAnnotation(Class<? extends Annotation> annotationClass) {
247+
return Pointcuts.union(new AnnotationMatchingPointcut(annotationClass, true),
248+
new AnnotationMatchingPointcut(null, annotationClass, true));
265249
}
266250

267251
}

config/src/test/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityConfigurationTests.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import org.junit.Test;
2626
import org.junit.runner.RunWith;
2727

28-
import org.springframework.aop.MethodMatcher;
28+
import org.springframework.aop.Pointcut;
2929
import org.springframework.aop.support.JdkRegexpMethodPointcut;
3030
import org.springframework.beans.factory.BeanCreationException;
3131
import org.springframework.beans.factory.annotation.Autowired;
@@ -353,12 +353,12 @@ static class CustomAuthorizationManagerAfterAdviceConfig {
353353

354354
@Bean
355355
AuthorizationMethodAfterAdvice<MethodAuthorizationContext> customAfterAdvice() {
356-
JdkRegexpMethodPointcut methodMatcher = new JdkRegexpMethodPointcut();
357-
methodMatcher.setPattern(".*MethodSecurityServiceImpl.*securedUser");
356+
JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
357+
pointcut.setPattern(".*MethodSecurityServiceImpl.*securedUser");
358358
return new AuthorizationMethodAfterAdvice<MethodAuthorizationContext>() {
359359
@Override
360-
public MethodMatcher getMethodMatcher() {
361-
return methodMatcher;
360+
public Pointcut getPointcut() {
361+
return pointcut;
362362
}
363363

364364
@Override

core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerMethodAfterAdvice.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.function.Supplier;
2020

2121
import org.springframework.aop.MethodMatcher;
22+
import org.springframework.aop.Pointcut;
2223
import org.springframework.security.access.AccessDeniedException;
2324
import org.springframework.security.authorization.AuthorizationManager;
2425
import org.springframework.security.core.Authentication;
@@ -31,24 +32,24 @@
3132
*
3233
* @param <T> the type of object that the authorization check is being done one.
3334
* @author Evgeniy Cheban
35+
* @author Josh Cummings
3436
* @since 5.5
3537
*/
3638
public final class AuthorizationManagerMethodAfterAdvice<T> implements AuthorizationMethodAfterAdvice<T> {
3739

38-
private final MethodMatcher methodMatcher;
40+
private final Pointcut pointcut;
3941

4042
private final AuthorizationManager<T> authorizationManager;
4143

4244
/**
4345
* Creates an instance.
44-
* @param methodMatcher the {@link MethodMatcher} to use
46+
* @param pointcut the {@link Pointcut} to use
4547
* @param authorizationManager the {@link AuthorizationManager} to use
4648
*/
47-
public AuthorizationManagerMethodAfterAdvice(MethodMatcher methodMatcher,
48-
AuthorizationManager<T> authorizationManager) {
49-
Assert.notNull(methodMatcher, "methodMatcher cannot be null");
49+
public AuthorizationManagerMethodAfterAdvice(Pointcut pointcut, AuthorizationManager<T> authorizationManager) {
50+
Assert.notNull(pointcut, "pointcut cannot be null");
5051
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
51-
this.methodMatcher = methodMatcher;
52+
this.pointcut = pointcut;
5253
this.authorizationManager = authorizationManager;
5354
}
5455

@@ -65,9 +66,12 @@ public Object after(Supplier<Authentication> authentication, T object, Object re
6566
return returnedObject;
6667
}
6768

69+
/**
70+
* {@inheritDoc}
71+
*/
6872
@Override
69-
public MethodMatcher getMethodMatcher() {
70-
return this.methodMatcher;
73+
public Pointcut getPointcut() {
74+
return this.pointcut;
7175
}
7276

7377
}

core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerMethodBeforeAdvice.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.function.Supplier;
2020

2121
import org.springframework.aop.MethodMatcher;
22+
import org.springframework.aop.Pointcut;
2223
import org.springframework.security.access.AccessDeniedException;
2324
import org.springframework.security.authorization.AuthorizationManager;
2425
import org.springframework.security.core.Authentication;
@@ -31,24 +32,24 @@
3132
*
3233
* @param <T> the type of object that the authorization check is being done one.
3334
* @author Evgeniy Cheban
35+
* @author Josh Cummings
3436
* @since 5.5
3537
*/
3638
public final class AuthorizationManagerMethodBeforeAdvice<T> implements AuthorizationMethodBeforeAdvice<T> {
3739

38-
private final MethodMatcher methodMatcher;
40+
private final Pointcut pointcut;
3941

4042
private final AuthorizationManager<T> authorizationManager;
4143

4244
/**
4345
* Creates an instance.
44-
* @param methodMatcher the {@link MethodMatcher} to use
46+
* @param pointcut the {@link Pointcut} to use
4547
* @param authorizationManager the {@link AuthorizationManager} to use
4648
*/
47-
public AuthorizationManagerMethodBeforeAdvice(MethodMatcher methodMatcher,
48-
AuthorizationManager<T> authorizationManager) {
49-
Assert.notNull(methodMatcher, "methodMatcher cannot be null");
49+
public AuthorizationManagerMethodBeforeAdvice(Pointcut pointcut, AuthorizationManager<T> authorizationManager) {
50+
Assert.notNull(pointcut, "pointcut cannot be null");
5051
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
51-
this.methodMatcher = methodMatcher;
52+
this.pointcut = pointcut;
5253
this.authorizationManager = authorizationManager;
5354
}
5455

@@ -64,9 +65,12 @@ public void before(Supplier<Authentication> authentication, T object) {
6465
this.authorizationManager.verify(authentication, object);
6566
}
6667

68+
/**
69+
* {@inheritDoc}
70+
*/
6771
@Override
68-
public MethodMatcher getMethodMatcher() {
69-
return this.methodMatcher;
72+
public Pointcut getPointcut() {
73+
return this.pointcut;
7074
}
7175

7276
}

core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodAfterAdvice.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,40 @@
1818

1919
import java.util.function.Supplier;
2020

21+
import org.aopalliance.aop.Advice;
2122
import org.aopalliance.intercept.MethodInvocation;
2223

23-
import org.springframework.aop.ClassFilter;
24-
import org.springframework.aop.Pointcut;
24+
import org.springframework.aop.AfterAdvice;
25+
import org.springframework.aop.PointcutAdvisor;
26+
import org.springframework.aop.framework.AopInfrastructureBean;
2527
import org.springframework.security.core.Authentication;
2628

2729
/**
28-
* An Authorization advice that can determine if an {@link Authentication} has access to
29-
* the returned object from the {@link MethodInvocation}. The {@link #getMethodMatcher()}
30+
* An {@link Advice} which can determine if an {@link Authentication} has
31+
* access to the returned object from the {@link MethodInvocation}. {@link #getPointcut()}
3032
* describes when the advice applies for the method.
3133
*
3234
* @param <T> the type of object that the authorization check is being done one.
3335
* @author Evgeniy Cheban
36+
* @author Josh Cummings
3437
* @since 5.5
3538
*/
36-
public interface AuthorizationMethodAfterAdvice<T> extends Pointcut {
39+
public interface AuthorizationMethodAfterAdvice<T> extends AfterAdvice, PointcutAdvisor, AopInfrastructureBean {
3740

3841
/**
39-
* Returns the default {@link ClassFilter}.
40-
* @return the {@link ClassFilter#TRUE} to use
42+
* {@inheritDoc}
4143
*/
4244
@Override
43-
default ClassFilter getClassFilter() {
44-
return ClassFilter.TRUE;
45+
default boolean isPerInstance() {
46+
return true;
47+
}
48+
49+
/**
50+
* {@inheritDoc}
51+
*/
52+
@Override
53+
default Advice getAdvice() {
54+
return this;
4555
}
4656

4757
/**

core/src/main/java/org/springframework/security/authorization/method/AuthorizationMethodBeforeAdvice.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,38 @@
1818

1919
import java.util.function.Supplier;
2020

21-
import org.springframework.aop.ClassFilter;
22-
import org.springframework.aop.Pointcut;
21+
import org.aopalliance.aop.Advice;
22+
23+
import org.springframework.aop.BeforeAdvice;
24+
import org.springframework.aop.PointcutAdvisor;
25+
import org.springframework.aop.framework.AopInfrastructureBean;
2326
import org.springframework.security.core.Authentication;
2427

2528
/**
26-
* An advice which can determine if an {@link Authentication} has access to the {@link T}
27-
* object. The {@link #getMethodMatcher()} describes when the advice applies for the
28-
* method.
29+
* An {@link Advice} which can determine if an {@link Authentication} has access to the
30+
* {@link T} object. {@link #getPointcut()} describes when the advice applies.
2931
*
3032
* @param <T> the type of object that the authorization check is being done one.
3133
* @author Evgeniy Cheban
34+
* @author Josh Cummings
3235
* @since 5.5
3336
*/
34-
public interface AuthorizationMethodBeforeAdvice<T> extends Pointcut {
37+
public interface AuthorizationMethodBeforeAdvice<T> extends BeforeAdvice, PointcutAdvisor, AopInfrastructureBean {
38+
39+
/**
40+
* {@inheritDoc}
41+
*/
42+
@Override
43+
default boolean isPerInstance() {
44+
return true;
45+
}
3546

3647
/**
37-
* Returns the default {@link ClassFilter}.
38-
* @return the {@link ClassFilter#TRUE} to use
48+
* {@inheritDoc}
3949
*/
4050
@Override
41-
default ClassFilter getClassFilter() {
42-
return ClassFilter.TRUE;
51+
default Advice getAdvice() {
52+
return this;
4353
}
4454

4555
/**

0 commit comments

Comments
 (0)