Align Method Traversal with MergedAnnotations

Closes gh-16751
This commit is contained in:
Josh Cummings
2025-01-08 14:22:31 -07:00
parent 4993fa863a
commit dc2e1af2da
2 changed files with 115 additions and 7 deletions

View File

@@ -19,8 +19,10 @@ package org.springframework.security.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -29,6 +31,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.MethodClassKey;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
@@ -169,18 +172,15 @@ final class UniqueSecurityAnnotationScanner<A extends Annotation> extends Abstra
return Collections.emptyList();
}
classesToSkip.add(targetClass);
try {
Method methodToUse = targetClass.getDeclaredMethod(method.getName(), method.getParameterTypes());
Method methodToUse = findMethod(method, targetClass);
if (methodToUse != null) {
List<MergedAnnotation<A>> annotations = findDirectAnnotations(methodToUse);
if (!annotations.isEmpty()) {
return annotations;
}
}
catch (NoSuchMethodException ex) {
// move on
}
List<MergedAnnotation<A>> annotations = new ArrayList<>();
annotations.addAll(findClosestMethodAnnotations(method, targetClass.getSuperclass(), classesToSkip));
List<MergedAnnotation<A>> annotations = new ArrayList<>(
findClosestMethodAnnotations(method, targetClass.getSuperclass(), classesToSkip));
for (Class<?> inter : targetClass.getInterfaces()) {
annotations.addAll(findClosestMethodAnnotations(method, inter, classesToSkip));
}
@@ -212,4 +212,52 @@ final class UniqueSecurityAnnotationScanner<A extends Annotation> extends Abstra
.toList();
}
private static Method findMethod(Method method, Class<?> targetClass) {
for (Method candidate : targetClass.getDeclaredMethods()) {
if (candidate == method) {
return candidate;
}
if (isOverride(method, candidate)) {
return candidate;
}
}
return null;
}
private static boolean isOverride(Method rootMethod, Method candidateMethod) {
return (!Modifier.isPrivate(candidateMethod.getModifiers())
&& candidateMethod.getName().equals(rootMethod.getName())
&& hasSameParameterTypes(rootMethod, candidateMethod));
}
private static boolean hasSameParameterTypes(Method rootMethod, Method candidateMethod) {
if (candidateMethod.getParameterCount() != rootMethod.getParameterCount()) {
return false;
}
Class<?>[] rootParameterTypes = rootMethod.getParameterTypes();
Class<?>[] candidateParameterTypes = candidateMethod.getParameterTypes();
if (Arrays.equals(candidateParameterTypes, rootParameterTypes)) {
return true;
}
return hasSameGenericTypeParameters(rootMethod, candidateMethod, rootParameterTypes);
}
private static boolean hasSameGenericTypeParameters(Method rootMethod, Method candidateMethod,
Class<?>[] rootParameterTypes) {
Class<?> sourceDeclaringClass = rootMethod.getDeclaringClass();
Class<?> candidateDeclaringClass = candidateMethod.getDeclaringClass();
if (!candidateDeclaringClass.isAssignableFrom(sourceDeclaringClass)) {
return false;
}
for (int i = 0; i < rootParameterTypes.length; i++) {
Class<?> resolvedParameterType = ResolvableType.forMethodParameter(candidateMethod, i, sourceDeclaringClass)
.resolve();
if (rootParameterTypes[i] != resolvedParameterType) {
return false;
}
}
return true;
}
}