Skip to content

Commit 3468b7f

Browse files
committed
Merge branch '6.4.x'
2 parents 6ce8307 + dc2e1af commit 3468b7f

File tree

2 files changed

+115
-7
lines changed

2 files changed

+115
-7
lines changed

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

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020
import java.lang.reflect.AnnotatedElement;
2121
import java.lang.reflect.Executable;
2222
import java.lang.reflect.Method;
23+
import java.lang.reflect.Modifier;
2324
import java.lang.reflect.Parameter;
2425
import java.util.ArrayList;
26+
import java.util.Arrays;
2527
import java.util.Collections;
2628
import java.util.HashSet;
2729
import java.util.List;
@@ -30,6 +32,7 @@
3032
import java.util.concurrent.ConcurrentHashMap;
3133

3234
import org.springframework.core.MethodClassKey;
35+
import org.springframework.core.ResolvableType;
3336
import org.springframework.core.annotation.AnnotationConfigurationException;
3437
import org.springframework.core.annotation.MergedAnnotation;
3538
import org.springframework.core.annotation.MergedAnnotations;
@@ -221,18 +224,15 @@ private List<MergedAnnotation<A>> findClosestMethodAnnotations(Method method, Cl
221224
return Collections.emptyList();
222225
}
223226
classesToSkip.add(targetClass);
224-
try {
225-
Method methodToUse = targetClass.getDeclaredMethod(method.getName(), method.getParameterTypes());
227+
Method methodToUse = findMethod(method, targetClass);
228+
if (methodToUse != null) {
226229
List<MergedAnnotation<A>> annotations = findDirectAnnotations(methodToUse);
227230
if (!annotations.isEmpty()) {
228231
return annotations;
229232
}
230233
}
231-
catch (NoSuchMethodException ex) {
232-
// move on
233-
}
234-
List<MergedAnnotation<A>> annotations = new ArrayList<>();
235-
annotations.addAll(findClosestMethodAnnotations(method, targetClass.getSuperclass(), classesToSkip));
234+
List<MergedAnnotation<A>> annotations = new ArrayList<>(
235+
findClosestMethodAnnotations(method, targetClass.getSuperclass(), classesToSkip));
236236
for (Class<?> inter : targetClass.getInterfaces()) {
237237
annotations.addAll(findClosestMethodAnnotations(method, inter, classesToSkip));
238238
}
@@ -264,4 +264,52 @@ private List<MergedAnnotation<A>> findDirectAnnotations(AnnotatedElement element
264264
.toList();
265265
}
266266

267+
private static Method findMethod(Method method, Class<?> targetClass) {
268+
for (Method candidate : targetClass.getDeclaredMethods()) {
269+
if (candidate == method) {
270+
return candidate;
271+
}
272+
if (isOverride(method, candidate)) {
273+
return candidate;
274+
}
275+
}
276+
return null;
277+
}
278+
279+
private static boolean isOverride(Method rootMethod, Method candidateMethod) {
280+
return (!Modifier.isPrivate(candidateMethod.getModifiers())
281+
&& candidateMethod.getName().equals(rootMethod.getName())
282+
&& hasSameParameterTypes(rootMethod, candidateMethod));
283+
}
284+
285+
private static boolean hasSameParameterTypes(Method rootMethod, Method candidateMethod) {
286+
if (candidateMethod.getParameterCount() != rootMethod.getParameterCount()) {
287+
return false;
288+
}
289+
Class<?>[] rootParameterTypes = rootMethod.getParameterTypes();
290+
Class<?>[] candidateParameterTypes = candidateMethod.getParameterTypes();
291+
if (Arrays.equals(candidateParameterTypes, rootParameterTypes)) {
292+
return true;
293+
}
294+
return hasSameGenericTypeParameters(rootMethod, candidateMethod, rootParameterTypes);
295+
}
296+
297+
private static boolean hasSameGenericTypeParameters(Method rootMethod, Method candidateMethod,
298+
Class<?>[] rootParameterTypes) {
299+
300+
Class<?> sourceDeclaringClass = rootMethod.getDeclaringClass();
301+
Class<?> candidateDeclaringClass = candidateMethod.getDeclaringClass();
302+
if (!candidateDeclaringClass.isAssignableFrom(sourceDeclaringClass)) {
303+
return false;
304+
}
305+
for (int i = 0; i < rootParameterTypes.length; i++) {
306+
Class<?> resolvedParameterType = ResolvableType.forMethodParameter(candidateMethod, i, sourceDeclaringClass)
307+
.resolve();
308+
if (rootParameterTypes[i] != resolvedParameterType) {
309+
return false;
310+
}
311+
}
312+
return true;
313+
}
314+
267315
}

core/src/test/java/org/springframework/security/core/annotation/UniqueSecurityAnnotationScannerTests.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,30 @@ void scanParameterAnnotationWhenInterfaceNoAnnotationsThenException() throws Exc
295295
.isThrownBy(() -> this.parameterScanner.scan(parameter));
296296
}
297297

298+
// gh-16751
299+
@Test
300+
void scanWhenAnnotationOnParameterizedInterfaceTheLocates() throws Exception {
301+
Method method = MyServiceImpl.class.getDeclaredMethod("get", String.class);
302+
PreAuthorize pre = this.scanner.scan(method, method.getDeclaringClass());
303+
assertThat(pre).isNotNull();
304+
}
305+
306+
// gh-16751
307+
@Test
308+
void scanWhenAnnotationOnParameterizedSuperClassThenLocates() throws Exception {
309+
Method method = MyServiceImpl.class.getDeclaredMethod("getExt", Long.class);
310+
PreAuthorize pre = this.scanner.scan(method, method.getDeclaringClass());
311+
assertThat(pre).isNotNull();
312+
}
313+
314+
// gh-16751
315+
@Test
316+
void scanWhenAnnotationOnParameterizedMethodThenLocates() throws Exception {
317+
Method method = MyServiceImpl.class.getDeclaredMethod("getExtByClass", Class.class, Long.class);
318+
PreAuthorize pre = this.scanner.scan(method, method.getDeclaringClass());
319+
assertThat(pre).isNotNull();
320+
}
321+
298322
interface UserService {
299323

300324
void add(@CustomParameterAnnotation("one") String user);
@@ -681,4 +705,40 @@ private static class ClassInheritingAbstractClassNoAnnotations extends AbstractC
681705

682706
}
683707

708+
interface MyService<C, U> {
709+
710+
@PreAuthorize("thirty")
711+
C get(U u);
712+
713+
}
714+
715+
abstract static class MyServiceExt<T> implements MyService<Integer, String> {
716+
717+
@PreAuthorize("thirtyone")
718+
abstract T getExt(T t);
719+
720+
@PreAuthorize("thirtytwo")
721+
abstract <S extends Number> S getExtByClass(Class<S> clazz, T t);
722+
723+
}
724+
725+
static class MyServiceImpl extends MyServiceExt<Long> {
726+
727+
@Override
728+
public Integer get(final String s) {
729+
return 0;
730+
}
731+
732+
@Override
733+
Long getExt(Long o) {
734+
return 0L;
735+
}
736+
737+
@Override
738+
<S extends Number> S getExtByClass(Class<S> clazz, Long l) {
739+
return null;
740+
}
741+
742+
}
743+
684744
}

0 commit comments

Comments
 (0)