Skip to content

Commit 6dfd604

Browse files
committed
Support @AuthenticationPrincipal on interfaces
Closes gh-16177
1 parent ea53a49 commit 6dfd604

File tree

2 files changed

+60
-2
lines changed

2 files changed

+60
-2
lines changed

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

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,23 @@
1818

1919
import java.lang.annotation.Annotation;
2020
import java.lang.reflect.AnnotatedElement;
21+
import java.lang.reflect.Executable;
2122
import java.lang.reflect.Method;
2223
import java.lang.reflect.Parameter;
2324
import java.util.ArrayList;
2425
import java.util.Collections;
2526
import java.util.HashSet;
2627
import java.util.List;
2728
import java.util.Map;
29+
import java.util.Objects;
2830
import java.util.Set;
2931
import java.util.concurrent.ConcurrentHashMap;
3032

3133
import org.springframework.core.MethodClassKey;
34+
import org.springframework.core.MethodParameter;
35+
import org.springframework.core.annotation.AnnotatedMethod;
3236
import org.springframework.core.annotation.AnnotationConfigurationException;
37+
import org.springframework.core.annotation.AnnotationUtils;
3338
import org.springframework.core.annotation.MergedAnnotation;
3439
import org.springframework.core.annotation.MergedAnnotations;
3540
import org.springframework.core.annotation.RepeatableContainers;
@@ -103,11 +108,45 @@ final class UniqueSecurityAnnotationScanner<A extends Annotation> extends Abstra
103108
this.types = types;
104109
}
105110

111+
private A findMethodAnnotation(Class<A> annotationClass, Parameter p) {
112+
Executable executable = p.getDeclaringExecutable();
113+
if (executable instanceof Method method) {
114+
AnnotatedMethod annotatedMethod = new AnnotatedMethod(method);
115+
MethodParameter parameter = null;
116+
MethodParameter[] methodParameters = annotatedMethod.getMethodParameters();
117+
for (MethodParameter methodParameter : methodParameters) {
118+
if (p == methodParameter.getParameter()) {
119+
parameter = methodParameter;
120+
break;
121+
}
122+
}
123+
if (parameter == null) {
124+
return null;
125+
}
126+
A annotation = parameter.getParameterAnnotation(annotationClass);
127+
if (annotation != null) {
128+
return annotation;
129+
}
130+
Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
131+
for (Annotation toSearch : annotationsToSearch) {
132+
annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass);
133+
if (annotation != null) {
134+
return annotation;
135+
}
136+
}
137+
}
138+
return null;
139+
}
140+
106141
@Override
107142
MergedAnnotation<A> merge(AnnotatedElement element, Class<?> targetClass) {
108143
if (element instanceof Parameter parameter) {
109144
return this.uniqueParameterAnnotationCache.computeIfAbsent(parameter, (p) -> {
110-
List<MergedAnnotation<A>> annotations = findDirectAnnotations(p);
145+
List<MergedAnnotation<A>> annotations = this.types.stream()
146+
.map((c) -> findMethodAnnotation(c, p))
147+
.filter(Objects::nonNull)
148+
.map(MergedAnnotation::from)
149+
.toList();
111150
return requireUnique(p, annotations);
112151
});
113152
}

web/src/test/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolverTests.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,14 @@ public void resolveArgumentCustomMetaAnnotationTpl() throws Exception {
214214
.isEqualTo(this.expectedPrincipal);
215215
}
216216

217+
@Test
218+
public void resolveArgumentAnnotationFromInterface() {
219+
CustomUserPrincipal principal = new CustomUserPrincipal();
220+
setAuthenticationPrincipal(principal);
221+
assertThat(this.resolver.supportsParameter(getMethodParameter("getUserByInterface", CustomUserPrincipal.class)))
222+
.isTrue();
223+
}
224+
217225
private MethodParameter showUserNoAnnotation() {
218226
return getMethodParameter("showUserNoAnnotation", String.class);
219227
}
@@ -312,7 +320,18 @@ private void setAuthenticationPrincipal(Object principal) {
312320

313321
}
314322

315-
public static class TestController {
323+
interface UserApi {
324+
325+
String getUserByInterface(@AuthenticationPrincipal CustomUserPrincipal user);
326+
327+
}
328+
329+
public static class TestController implements UserApi {
330+
331+
@Override
332+
public String getUserByInterface(CustomUserPrincipal user) {
333+
return "";
334+
}
316335

317336
public void showUserNoAnnotation(String user) {
318337
}

0 commit comments

Comments
 (0)