-
Notifications
You must be signed in to change notification settings - Fork 38.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MergedAnnotations
finds duplicate annotations on method in multi-level interface hierarchy
#31803
Comments
MergedAnnotations
should not duplicate annotations
Related workaround in Spring Security: spring-projects/spring-security@be11812 |
Thanks for reporting the issue. 👍 I've been able to confirm it with the following standalone test class. package demo;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import org.junit.jupiter.api.Test;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.core.annotation.MergedAnnotations.search;
import static org.springframework.core.annotation.MergedAnnotations.SearchStrategy.TYPE_HIERARCHY;
class ReproTests {
@Test
void test() throws Exception {
Method method = HelloImpl.class.getMethod("sayHello");
Stream<PreAuthorize> stream = search(TYPE_HIERARCHY)
.from(method)
.stream(PreAuthorize.class)
.map(MergedAnnotation::synthesize);
assertThat(stream).hasSize(1);
}
interface Hello {
@PreAuthorize("demo")
void sayHello();
}
interface SayHello extends Hello {
}
static class HelloImpl implements SayHello {
@Override
public void sayHello() {
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface PreAuthorize {
String value();
}
} The above fails with:
Furthermore, I verified that the test passes if |
MergedAnnotations
should not duplicate annotationsMergedAnnotations
finds duplicate annotations on methods in multi-level interface hierarchy
MergedAnnotations
finds duplicate annotations on methods in multi-level interface hierarchyMergedAnnotations
finds duplicate annotations on method in multi-level interface hierarchy
Prior to this commit, the AnnotationsScanner used in the MergedAnnotations infrastructure found duplicate annotations on methods within multi-level interface hierarchies. This commit addresses this issue by scanning methods at a given level in the interface hierarchy using ReflectionUtils#getDeclaredMethods instead of Class#getMethods, since the latter includes public methods declared in super-interfaces which will anyway be scanned when processing super-interfaces recursively. Closes gh-31803 (cherry picked from commit 75da9c3)
Prior to this commit, the AnnotationsScanner used in the MergedAnnotations infrastructure found duplicate annotations on methods within multi-level interface hierarchies. This commit addresses this issue by scanning methods at a given level in the interface hierarchy using ReflectionUtils#getDeclaredMethods instead of Class#getMethods, since the latter includes public methods declared in super-interfaces which will anyway be scanned when processing super-interfaces recursively. Closes gh-31803 (cherry picked from commit 75da9c3) (cherry picked from commit 1e742aa)
This has been fixed in 75da9c3 for inclusion in the upcoming Spring Framework 6.1.2 release. Because this bug has existed since 5.2 when the |
This commit revises AuthorizationAnnotationUtils as follows. - Removes code duplication by treating both Class and Method as AnnotatedElement. - Avoids duplicated annotation searches by processing merged annotations in a single Stream instead of first using the MergedAnnotations API to find possible duplicates and then again searching for a single annotation via AnnotationUtils (which effectively performs the same search using the MergedAnnotations API internally). - Uses `.distinct()` within the Stream to avoid the need for the workaround introduced in spring-projectsgh-13625. Note that the semantics here result in duplicate "equivalent" annotations being ignored. In other words, if @PreAuthorize("hasRole('someRole')") is present multiple times as a meta-annotation, no exception will be thrown and the first such annotation found will be used. - Improves the error message when competing annotations are found by including the competing annotations in the error message. - Updates AuthorizationAnnotationUtilsTests to cover all known, supported use cases. - Configures correct role in @RequireUserRole. Please note this commit uses `.map(MergedAnnotation::withNonMergedAttributes)` to retain backward compatibility with previous versions of Spring Security. However, that line can be deleted if the Spring Security team decides that it wishes to support merged annotation attributes via custom composed annotations. If that decision is made, the composedMergedAnnotationsAreNotSupported() test should be renamed and updated as explained in the comment in that method. See spring-projectsgh-13625 See spring-projects/spring-framework#31803
This commit revises AuthorizationAnnotationUtils as follows. - Removes code duplication by treating both Class and Method as AnnotatedElement. - Avoids duplicated annotation searches by processing merged annotations in a single Stream instead of first using the MergedAnnotations API to find possible duplicates and then again searching for a single annotation via AnnotationUtils (which effectively performs the same search using the MergedAnnotations API internally). - Uses `.distinct()` within the Stream to avoid the need for the workaround introduced in gh-13625. Note that the semantics here result in duplicate "equivalent" annotations being ignored. In other words, if @PreAuthorize("hasRole('someRole')") is present multiple times as a meta-annotation, no exception will be thrown and the first such annotation found will be used. - Improves the error message when competing annotations are found by including the competing annotations in the error message. - Updates AuthorizationAnnotationUtilsTests to cover all known, supported use cases. - Configures correct role in @RequireUserRole. Please note this commit uses `.map(MergedAnnotation::withNonMergedAttributes)` to retain backward compatibility with previous versions of Spring Security. However, that line can be deleted if the Spring Security team decides that it wishes to support merged annotation attributes via custom composed annotations. If that decision is made, the composedMergedAnnotationsAreNotSupported() test should be renamed and updated as explained in the comment in that method. See gh-13625 See spring-projects/spring-framework#31803
Related to spring-projects/spring-security#13625 (GitHub reproducer)
In a hierarchy like this:
a call to
MergedAnnoatations
like this:will return multiple instances of
MergedAnnotation
forPreAuthorize.class
.It's expected that such an arrangement would only produce one
MergedAnnotation
instance since there is only one in the hierarchy.Thanks to @philwebb for helping me find a workaround. Since
PreAuthorize
is not a repeatable annotation, Spring Security can ignore subsequentMergedAnnotation
instances from the sameMergedAnnotation#getSource
.The text was updated successfully, but these errors were encountered: