Skip to content

Commit b3bb20d

Browse files
committed
Adjust Annotation Scanning
Closes spring-projectsgh-15097
1 parent a6b55f1 commit b3bb20d

12 files changed

+595
-111
lines changed

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
package org.springframework.security.authorization.method;
1818

1919
import java.lang.annotation.Annotation;
20+
import java.lang.reflect.AnnotatedElement;
2021
import java.lang.reflect.Method;
22+
import java.util.function.Function;
2123

2224
import org.springframework.security.core.annotation.AnnotationSynthesizers;
2325

@@ -44,12 +46,16 @@
4446
*/
4547
final class AuthorizationAnnotationUtils {
4648

49+
static <A extends Annotation> Function<AnnotatedElement, A> withDefaults(Class<A> type) {
50+
return AnnotationSynthesizers.requireUnique(type)::synthesize;
51+
}
52+
4753
static <A extends Annotation> A findUniqueAnnotation(Method method, Class<A> annotationType) {
48-
return AnnotationSynthesizers.createDefault(annotationType).synthesize(method);
54+
return AnnotationSynthesizers.requireUnique(annotationType).synthesize(method);
4955
}
5056

5157
static <A extends Annotation> A findUniqueAnnotation(Class<?> type, Class<A> annotationType) {
52-
return AnnotationSynthesizers.createDefault(annotationType).synthesize(type);
58+
return AnnotationSynthesizers.requireUnique(annotationType).synthesize(type);
5359
}
5460

5561
private AuthorizationAnnotationUtils() {

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

Lines changed: 10 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.lang.reflect.Method;
2121
import java.util.Collection;
2222
import java.util.HashSet;
23+
import java.util.List;
2324
import java.util.Set;
2425
import java.util.function.Supplier;
2526

@@ -29,7 +30,6 @@
2930
import org.aopalliance.intercept.MethodInvocation;
3031

3132
import org.springframework.aop.support.AopUtils;
32-
import org.springframework.core.annotation.AnnotationConfigurationException;
3333
import org.springframework.lang.NonNull;
3434
import org.springframework.security.authorization.AuthoritiesAuthorizationManager;
3535
import org.springframework.security.authorization.AuthorizationDecision;
@@ -54,9 +54,9 @@ public final class Jsr250AuthorizationManager implements AuthorizationManager<Me
5454
private static final Set<AnnotationSynthesizer<? extends Annotation>> JSR250_ANNOTATIONS = new HashSet<>();
5555

5656
static {
57-
JSR250_ANNOTATIONS.add(AnnotationSynthesizers.createDefault(DenyAll.class));
58-
JSR250_ANNOTATIONS.add(AnnotationSynthesizers.createDefault(PermitAll.class));
59-
JSR250_ANNOTATIONS.add(AnnotationSynthesizers.createDefault(RolesAllowed.class));
57+
JSR250_ANNOTATIONS.add(AnnotationSynthesizers.requireUnique(DenyAll.class));
58+
JSR250_ANNOTATIONS.add(AnnotationSynthesizers.requireUnique(PermitAll.class));
59+
JSR250_ANNOTATIONS.add(AnnotationSynthesizers.requireUnique(RolesAllowed.class));
6060
}
6161

6262
private final Jsr250AuthorizationManagerRegistry registry = new Jsr250AuthorizationManagerRegistry();
@@ -104,6 +104,9 @@ public AuthorizationDecision check(Supplier<Authentication> authentication, Meth
104104

105105
private final class Jsr250AuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry {
106106

107+
private final AnnotationSynthesizer<?> synthesizer = AnnotationSynthesizers
108+
.requireUnique(List.of(DenyAll.class, PermitAll.class, RolesAllowed.class));
109+
107110
@NonNull
108111
@Override
109112
AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass) {
@@ -123,45 +126,9 @@ AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> ta
123126

124127
private Annotation findJsr250Annotation(Method method, Class<?> targetClass) {
125128
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
126-
Annotation annotation = findAnnotation(specificMethod);
127-
return (annotation != null) ? annotation
128-
: findAnnotation((targetClass != null) ? targetClass : specificMethod.getDeclaringClass());
129-
}
130-
131-
private Annotation findAnnotation(Method method) {
132-
Set<Annotation> annotations = new HashSet<>();
133-
for (AnnotationSynthesizer<? extends Annotation> synthesizer : JSR250_ANNOTATIONS) {
134-
Annotation annotation = synthesizer.synthesize(method);
135-
if (annotation != null) {
136-
annotations.add(annotation);
137-
}
138-
}
139-
if (annotations.isEmpty()) {
140-
return null;
141-
}
142-
if (annotations.size() > 1) {
143-
throw new AnnotationConfigurationException(
144-
"The JSR-250 specification disallows DenyAll, PermitAll, and RolesAllowed from appearing on the same method.");
145-
}
146-
return annotations.iterator().next();
147-
}
148-
149-
private Annotation findAnnotation(Class<?> clazz) {
150-
Set<Annotation> annotations = new HashSet<>();
151-
for (AnnotationSynthesizer<? extends Annotation> synthesizer : JSR250_ANNOTATIONS) {
152-
Annotation annotation = synthesizer.synthesize(clazz);
153-
if (annotation != null) {
154-
annotations.add(annotation);
155-
}
156-
}
157-
if (annotations.isEmpty()) {
158-
return null;
159-
}
160-
if (annotations.size() > 1) {
161-
throw new AnnotationConfigurationException(
162-
"The JSR-250 specification disallows DenyAll, PermitAll, and RolesAllowed from appearing on the same class definition.");
163-
}
164-
return annotations.iterator().next();
129+
Annotation annotation = this.synthesizer.synthesize(specificMethod);// findAnnotation(specificMethod);
130+
return (annotation != null) ? annotation : this.synthesizer
131+
.synthesize((targetClass != null) ? targetClass : specificMethod.getDeclaringClass());
165132
}
166133

167134
private Set<String> getAllowedRolesWithPrefix(RolesAllowed rolesAllowed) {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ final class PostAuthorizeExpressionAttributeRegistry extends AbstractExpressionA
4242
private final MethodAuthorizationDeniedHandler defaultHandler = new ThrowingMethodAuthorizationDeniedHandler();
4343

4444
private final AnnotationSynthesizer<HandleAuthorizationDenied> handleAuthorizationDeniedSynthesizer = AnnotationSynthesizers
45-
.createDefault(HandleAuthorizationDenied.class);
45+
.requireUnique(HandleAuthorizationDenied.class);
4646

4747
private Function<Class<? extends MethodAuthorizationDeniedHandler>, MethodAuthorizationDeniedHandler> handlerResolver;
4848

4949
private AnnotationSynthesizer<PostAuthorize> postAuthorizeSynthesizer = AnnotationSynthesizers
50-
.createDefault(PostAuthorize.class);
50+
.requireUnique(PostAuthorize.class);
5151

5252
PostAuthorizeExpressionAttributeRegistry() {
5353
this.handlerResolver = (clazz) -> this.defaultHandler;
@@ -95,7 +95,7 @@ void setApplicationContext(ApplicationContext context) {
9595
}
9696

9797
void setTemplateDefaults(PrePostTemplateDefaults templateDefaults) {
98-
this.postAuthorizeSynthesizer = AnnotationSynthesizers.createDefault(PostAuthorize.class, templateDefaults);
98+
this.postAuthorizeSynthesizer = AnnotationSynthesizers.requireUnique(PostAuthorize.class, templateDefaults);
9999
}
100100

101101
private MethodAuthorizationDeniedHandler resolveHandler(ApplicationContext context,

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
*/
3535
final class PostFilterExpressionAttributeRegistry extends AbstractExpressionAttributeRegistry<ExpressionAttribute> {
3636

37-
private AnnotationSynthesizer<PostFilter> synthesizer = AnnotationSynthesizers.createDefault(PostFilter.class);
37+
private AnnotationSynthesizer<PostFilter> synthesizer = AnnotationSynthesizers.requireUnique(PostFilter.class);
3838

3939
@NonNull
4040
@Override
@@ -50,7 +50,7 @@ ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) {
5050
}
5151

5252
void setTemplateDefaults(PrePostTemplateDefaults defaults) {
53-
this.synthesizer = AnnotationSynthesizers.createDefault(PostFilter.class, defaults);
53+
this.synthesizer = AnnotationSynthesizers.requireUnique(PostFilter.class, defaults);
5454
}
5555

5656
private PostFilter findPostFilterAnnotation(Method method, Class<?> targetClass) {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ final class PreAuthorizeExpressionAttributeRegistry extends AbstractExpressionAt
4242
private final MethodAuthorizationDeniedHandler defaultHandler = new ThrowingMethodAuthorizationDeniedHandler();
4343

4444
private final AnnotationSynthesizer<HandleAuthorizationDenied> handleAuthorizationDeniedSynthesizer = AnnotationSynthesizers
45-
.createDefault(HandleAuthorizationDenied.class);
45+
.requireUnique(HandleAuthorizationDenied.class);
4646

4747
private Function<Class<? extends MethodAuthorizationDeniedHandler>, MethodAuthorizationDeniedHandler> handlerResolver;
4848

4949
private AnnotationSynthesizer<PreAuthorize> preAuthorizeSynthesizer = AnnotationSynthesizers
50-
.createDefault(PreAuthorize.class);
50+
.requireUnique(PreAuthorize.class);
5151

5252
PreAuthorizeExpressionAttributeRegistry() {
5353
this.handlerResolver = (clazz) -> this.defaultHandler;
@@ -95,7 +95,7 @@ void setApplicationContext(ApplicationContext context) {
9595
}
9696

9797
void setTemplateDefaults(PrePostTemplateDefaults defaults) {
98-
this.preAuthorizeSynthesizer = AnnotationSynthesizers.createDefault(PreAuthorize.class, defaults);
98+
this.preAuthorizeSynthesizer = AnnotationSynthesizers.requireUnique(PreAuthorize.class, defaults);
9999
}
100100

101101
private MethodAuthorizationDeniedHandler resolveHandler(ApplicationContext context,

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
final class PreFilterExpressionAttributeRegistry
3636
extends AbstractExpressionAttributeRegistry<PreFilterExpressionAttributeRegistry.PreFilterExpressionAttribute> {
3737

38-
private AnnotationSynthesizer<PreFilter> synthesizer = AnnotationSynthesizers.createDefault(PreFilter.class);
38+
private AnnotationSynthesizer<PreFilter> synthesizer = AnnotationSynthesizers.requireUnique(PreFilter.class);
3939

4040
@NonNull
4141
@Override
@@ -51,7 +51,7 @@ PreFilterExpressionAttribute resolveAttribute(Method method, Class<?> targetClas
5151
}
5252

5353
void setTemplateDefaults(PrePostTemplateDefaults defaults) {
54-
this.synthesizer = AnnotationSynthesizers.createDefault(PreFilter.class, defaults);
54+
this.synthesizer = AnnotationSynthesizers.requireUnique(PreFilter.class, defaults);
5555
}
5656

5757
private PreFilter findPreFilterAnnotation(Method method, Class<?> targetClass) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public final class SecuredAuthorizationManager implements AuthorizationManager<M
5252

5353
private final Map<MethodClassKey, Set<String>> cachedAuthorities = new ConcurrentHashMap<>();
5454

55-
private final AnnotationSynthesizer<Secured> synthesizer = AnnotationSynthesizers.createDefault(Secured.class);
55+
private final AnnotationSynthesizer<Secured> synthesizer = AnnotationSynthesizers.requireUnique(Secured.class);
5656

5757
/**
5858
* Sets an {@link AuthorizationManager} that accepts a collection of authority

core/src/main/java/org/springframework/security/core/annotation/AnnotationSynthesizer.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.lang.annotation.Annotation;
2020
import java.lang.reflect.AnnotatedElement;
2121

22+
import org.springframework.core.annotation.MergedAnnotation;
2223
import org.springframework.lang.Nullable;
2324

2425
/**
@@ -59,6 +60,14 @@ public interface AnnotationSynthesizer<A extends Annotation> {
5960
* @return the synthesized annotation or {@code null} if not found
6061
*/
6162
@Nullable
62-
A synthesize(AnnotatedElement element);
63+
default A synthesize(AnnotatedElement element) {
64+
MergedAnnotation<A> annotation = merge(element);
65+
if (annotation == null) {
66+
return null;
67+
}
68+
return annotation.synthesize();
69+
}
70+
71+
MergedAnnotation<A> merge(AnnotatedElement element);
6372

6473
}

core/src/main/java/org/springframework/security/core/annotation/AnnotationSynthesizers.java

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import java.lang.annotation.Annotation;
2020
import java.lang.reflect.AnnotatedElement;
21+
import java.util.ArrayList;
22+
import java.util.List;
2123

2224
/**
2325
* Factory for creating {@link AnnotationSynthesizer} instances.
@@ -31,38 +33,49 @@ private AnnotationSynthesizers() {
3133
}
3234

3335
/**
34-
* Create a default {@link AnnotationSynthesizer} for the given annotation type.
35-
*
36-
* <p>
37-
* By default, uses an annotation synthsizer that ensures the annotation's uniqueness
38-
* on the {@link AnnotatedElement}.
36+
* Create a {@link AnnotationSynthesizer} that requires synthesized annotations to be
37+
* unique on the given {@link AnnotatedElement}.
3938
* @param type the annotation type
4039
* @param <A> the annotation type
4140
* @return the default {@link AnnotationSynthesizer}
4241
*/
43-
public static <A extends Annotation> AnnotationSynthesizer<A> createDefault(Class<A> type) {
42+
public static <A extends Annotation> AnnotationSynthesizer<A> requireUnique(Class<A> type) {
4443
return new UniqueMergedAnnotationSynthesizer<>(type);
4544
}
4645

4746
/**
48-
* Create a default {@link AnnotationSynthesizer} for the given annotation type.
47+
* Create a {@link AnnotationSynthesizer} that requires synthesized annotations to be
48+
* unique on the given {@link AnnotatedElement}.
4949
*
5050
* <p>
5151
* When a {@link AnnotationTemplateExpressionDefaults} is provided, it will return a
52-
* synethsizer that supports placeholders in the annotation's attributes in addition
53-
* to the meta-annotation sythesizing provided by {@link #createDefault(Class)}.
52+
* synthesizer that supports placeholders in the annotation's attributes in addition
53+
* to the meta-annotation synthesizing provided by {@link #requireUnique(Class)}.
5454
* @param type the annotation type
5555
* @param templateDefaults the defaults for resolving placeholders in the annotation's
5656
* attributes
5757
* @param <A> the annotation type
5858
* @return the default {@link AnnotationSynthesizer}
5959
*/
60-
public static <A extends Annotation> AnnotationSynthesizer<A> createDefault(Class<A> type,
60+
public static <A extends Annotation> AnnotationSynthesizer<A> requireUnique(Class<A> type,
6161
AnnotationTemplateExpressionDefaults templateDefaults) {
6262
if (templateDefaults == null) {
6363
return new UniqueMergedAnnotationSynthesizer<>(type);
6464
}
6565
return new ExpressionTemplateAnnotationSynthesizer<>(type, templateDefaults);
6666
}
6767

68+
/**
69+
* Create a {@link AnnotationSynthesizer} that requires synthesized annotations to be
70+
* unique on the given {@link AnnotatedElement}. Supplying multiple types implies that
71+
* the synthesized annotation must be unique across all specified types.
72+
* @param types the annotation types
73+
* @return the default {@link AnnotationSynthesizer}
74+
*/
75+
public static AnnotationSynthesizer<Annotation> requireUnique(List<Class<? extends Annotation>> types) {
76+
List<Class<Annotation>> casted = new ArrayList<>();
77+
types.forEach(type -> casted.add((Class<Annotation>) type));
78+
return new UniqueMergedAnnotationSynthesizer<>(casted);
79+
}
80+
6881
}

core/src/main/java/org/springframework/security/core/annotation/ExpressionTemplateAnnotationSynthesizer.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import java.util.Map;
2323

2424
import org.springframework.core.annotation.MergedAnnotation;
25-
import org.springframework.core.annotation.MergedAnnotations;
2625
import org.springframework.core.convert.support.DefaultConversionService;
2726
import org.springframework.util.Assert;
2827
import org.springframework.util.PropertyPlaceholderHelper;
@@ -64,7 +63,7 @@ final class ExpressionTemplateAnnotationSynthesizer<A extends Annotation> implem
6463

6564
private final AnnotationTemplateExpressionDefaults templateDefaults;
6665

67-
private final Map<AnnotatedElement, A> uniqueAnnotationCache = new HashMap<>();
66+
private final Map<AnnotatedElement, MergedAnnotation<A>> uniqueAnnotationCache = new HashMap<>();
6867

6968
ExpressionTemplateAnnotationSynthesizer(Class<A> type, AnnotationTemplateExpressionDefaults templateDefaults) {
7069
Assert.notNull(type, "type cannot be null");
@@ -75,17 +74,20 @@ final class ExpressionTemplateAnnotationSynthesizer<A extends Annotation> implem
7574
}
7675

7776
@Override
78-
public A synthesize(AnnotatedElement element) {
79-
this.uniqueAnnotationCache.computeIfAbsent(element, this.unique::synthesize);
80-
return resolvePlaceholders(MergedAnnotations.from(element).get(this.type));
77+
public MergedAnnotation<A> merge(AnnotatedElement element) {
78+
MergedAnnotation<A> annotation = this.uniqueAnnotationCache.computeIfAbsent(element, this.unique::merge);
79+
if (annotation == null) {
80+
return null;
81+
}
82+
return resolvePlaceholders(annotation);
8183
}
8284

83-
private A resolvePlaceholders(MergedAnnotation<A> mergedAnnotation) {
85+
private MergedAnnotation<A> resolvePlaceholders(MergedAnnotation<A> mergedAnnotation) {
8486
if (this.templateDefaults == null) {
85-
return mergedAnnotation.synthesize();
87+
return mergedAnnotation;
8688
}
8789
if (mergedAnnotation.getMetaSource() == null) {
88-
return mergedAnnotation.synthesize();
90+
return mergedAnnotation;
8991
}
9092
PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("{", "}", null, null,
9193
this.templateDefaults.isIgnoreUnknown());
@@ -109,7 +111,7 @@ private A resolvePlaceholders(MergedAnnotation<A> mergedAnnotation) {
109111
properties.put(annotationProperty.getKey(), value);
110112
}
111113
AnnotatedElement annotatedElement = (AnnotatedElement) mergedAnnotation.getSource();
112-
return MergedAnnotation.of(annotatedElement, this.type, properties).synthesize();
114+
return MergedAnnotation.of(annotatedElement, this.type, properties);
113115
}
114116

115117
}

0 commit comments

Comments
 (0)