Skip to content

Commit 7d81a52

Browse files
committed
Allow AuthenticationPrincipal argument type to be primitive
Closes spring-projectsgh-10172
1 parent 7112ee3 commit 7d81a52

File tree

8 files changed

+102
-12
lines changed

8 files changed

+102
-12
lines changed

messaging/src/main/java/org/springframework/security/messaging/context/AuthenticationPrincipalArgumentResolver.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
3030
import org.springframework.security.core.annotation.AuthenticationPrincipal;
3131
import org.springframework.security.core.context.SecurityContextHolder;
3232
import org.springframework.stereotype.Controller;
33+
import org.springframework.util.ClassUtils;
3334
import org.springframework.util.StringUtils;
3435

3536
/**
@@ -107,7 +108,7 @@ public Object resolveArgument(MethodParameter parameter, Message<?> message) {
107108
Expression expression = this.parser.parseExpression(expressionToParse);
108109
principal = expression.getValue(context);
109110
}
110-
if (principal != null && !parameter.getParameterType().isAssignableFrom(principal.getClass())) {
111+
if (principal != null && !ClassUtils.isAssignable(parameter.getParameterType(), principal.getClass())) {
111112
if (authPrincipal.errorOnInvalidType()) {
112113
throw new ClassCastException(principal + " is not assignable to " + parameter.getParameterType());
113114
}

messaging/src/main/java/org/springframework/security/messaging/handler/invocation/reactive/AuthenticationPrincipalArgumentResolver.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019 the original author or authors.
2+
* Copyright 2019-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@
3939
import org.springframework.security.core.context.SecurityContext;
4040
import org.springframework.stereotype.Controller;
4141
import org.springframework.util.Assert;
42+
import org.springframework.util.ClassUtils;
4243
import org.springframework.util.StringUtils;
4344

4445
/**
@@ -170,7 +171,7 @@ private boolean isInvalidType(MethodParameter parameter, Object principal) {
170171
}
171172
typeToCheck = genericType;
172173
}
173-
return !typeToCheck.isAssignableFrom(principal.getClass());
174+
return !ClassUtils.isAssignable(typeToCheck, principal.getClass());
174175
}
175176

176177
/**

messaging/src/test/java/org/springframework/security/messaging/context/AuthenticationPrincipalArgumentResolverTests.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -133,6 +133,14 @@ public void resolveArgumentSpelCopy() throws Exception {
133133
assertThat(resolveArgument).isNotSameAs(principal);
134134
}
135135

136+
@Test
137+
public void resolveArgumentSpelPrimitive() throws Exception {
138+
CustomUserPrincipal principal = new CustomUserPrincipal();
139+
setAuthenticationPrincipal(principal);
140+
this.expectedPrincipal = principal.id;
141+
assertThat(this.resolver.resolveArgument(showUserSpelPrimitive(), null)).isEqualTo(this.expectedPrincipal);
142+
}
143+
136144
@Test
137145
public void resolveArgumentNullOnInvalidType() throws Exception {
138146
setAuthenticationPrincipal(new CustomUserPrincipal());
@@ -195,6 +203,10 @@ private MethodParameter showUserSpelCopy() {
195203
return getMethodParameter("showUserSpelCopy", CopyUserPrincipal.class);
196204
}
197205

206+
private MethodParameter showUserSpelPrimitive() {
207+
return getMethodParameter("showUserSpelPrimitive", int.class);
208+
}
209+
198210
private MethodParameter showUserAnnotationObject() {
199211
return getMethodParameter("showUserAnnotation", Object.class);
200212
}
@@ -258,12 +270,17 @@ public void showUserSpelCopy(@AuthenticationPrincipal(
258270
expression = "new org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolverTests$CopyUserPrincipal(#this)") CopyUserPrincipal user) {
259271
}
260272

273+
public void showUserSpelPrimitive(@AuthenticationPrincipal(expression = "id") int id) {
274+
}
275+
261276
}
262277

263278
static class CustomUserPrincipal {
264279

265280
public final String property = "property";
266281

282+
public final int id = 1;
283+
267284
}
268285

269286
public static class CopyUserPrincipal {

messaging/src/test/java/org/springframework/security/messaging/handler/invocation/reactive/AuthenticationPrincipalArgumentResolverTests.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019 the original author or authors.
2+
* Copyright 2019-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525
import org.springframework.core.MethodParameter;
2626
import org.springframework.core.annotation.SynthesizingMethodParameter;
2727
import org.springframework.security.authentication.TestAuthentication;
28+
import org.springframework.security.authentication.TestingAuthenticationToken;
2829
import org.springframework.security.core.Authentication;
2930
import org.springframework.security.core.annotation.AuthenticationPrincipal;
3031
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
@@ -105,6 +106,21 @@ private void authenticationPrincipalExpression(
105106
@AuthenticationPrincipal(expression = "username") Mono<String> username) {
106107
}
107108

109+
@Test
110+
public void resolveArgumentWhenExpressionPrimitiveThenFound() {
111+
CustomUserPrincipal principal = new CustomUserPrincipal();
112+
// @formatter:off
113+
Mono<Object> result = this.resolver
114+
.resolveArgument(arg0("authenticationPrincipalExpressionPrimitive"), null)
115+
.subscriberContext(ReactiveSecurityContextHolder.withAuthentication(new TestingAuthenticationToken(principal, "password", "ROLE_USER")));
116+
// @formatter:on
117+
assertThat(result.block()).isEqualTo(principal.id);
118+
}
119+
120+
@SuppressWarnings("unused")
121+
private void authenticationPrincipalExpressionPrimitive(@AuthenticationPrincipal(expression = "id") int username) {
122+
}
123+
108124
@Test
109125
public void supportsParameterWhenNotAnnotatedThenFalse() {
110126
assertThat(this.resolver.supportsParameter(arg0("monoUserDetails"))).isFalse();
@@ -125,4 +141,10 @@ private MethodParameter arg0(String methodName) {
125141

126142
}
127143

144+
static class CustomUserPrincipal {
145+
146+
public final int id = 1;
147+
148+
}
149+
128150
}

web/src/main/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolver.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
2929
import org.springframework.security.core.annotation.AuthenticationPrincipal;
3030
import org.springframework.security.core.context.SecurityContextHolder;
3131
import org.springframework.stereotype.Controller;
32+
import org.springframework.util.ClassUtils;
3233
import org.springframework.util.StringUtils;
3334
import org.springframework.web.bind.support.WebDataBinderFactory;
3435
import org.springframework.web.context.request.NativeWebRequest;
@@ -114,7 +115,7 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m
114115
Expression expression = this.parser.parseExpression(expressionToParse);
115116
principal = expression.getValue(context);
116117
}
117-
if (principal != null && !parameter.getParameterType().isAssignableFrom(principal.getClass())) {
118+
if (principal != null && !ClassUtils.isAssignable(parameter.getParameterType(), principal.getClass())) {
118119
if (annotation.errorOnInvalidType()) {
119120
throw new ClassCastException(principal + " is not assignable to " + parameter.getParameterType());
120121
}

web/src/main/java/org/springframework/security/web/reactive/result/method/annotation/AuthenticationPrincipalArgumentResolver.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@
3434
import org.springframework.security.core.annotation.AuthenticationPrincipal;
3535
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
3636
import org.springframework.security.core.context.SecurityContext;
37+
import org.springframework.util.ClassUtils;
3738
import org.springframework.util.StringUtils;
3839
import org.springframework.web.reactive.BindingContext;
3940
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport;
@@ -114,7 +115,7 @@ private boolean isInvalidType(MethodParameter parameter, Object principal) {
114115
}
115116
typeToCheck = genericType;
116117
}
117-
return !typeToCheck.isAssignableFrom(principal.getClass());
118+
return !ClassUtils.isAssignable(typeToCheck, principal.getClass());
118119
}
119120

120121
/**

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -157,6 +157,15 @@ public void resolveArgumentSpelCopy() throws Exception {
157157
assertThat(resolveArgument).isNotSameAs(principal);
158158
}
159159

160+
@Test
161+
public void resolveArgumentSpelPrimitive() throws Exception {
162+
CustomUserPrincipal principal = new CustomUserPrincipal();
163+
setAuthenticationPrincipal(principal);
164+
this.expectedPrincipal = principal.id;
165+
assertThat(this.resolver.resolveArgument(showUserSpelPrimitive(), null, null, null))
166+
.isEqualTo(this.expectedPrincipal);
167+
}
168+
160169
@Test
161170
public void resolveArgumentNullOnInvalidType() throws Exception {
162171
setAuthenticationPrincipal(new CustomUserPrincipal());
@@ -224,6 +233,10 @@ private MethodParameter showUserSpelCopy() {
224233
return getMethodParameter("showUserSpelCopy", CopyUserPrincipal.class);
225234
}
226235

236+
private MethodParameter showUserSpelPrimitive() {
237+
return getMethodParameter("showUserSpelPrimitive", int.class);
238+
}
239+
227240
private MethodParameter showUserAnnotationObject() {
228241
return getMethodParameter("showUserAnnotation", Object.class);
229242
}
@@ -290,12 +303,17 @@ public void showUserSpelCopy(@AuthenticationPrincipal(
290303
expression = "new org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolverTests$CopyUserPrincipal(#this)") CopyUserPrincipal user) {
291304
}
292305

306+
public void showUserSpelPrimitive(@AuthenticationPrincipal(expression = "id") int id) {
307+
}
308+
293309
}
294310

295311
static class CustomUserPrincipal {
296312

297313
public final String property = "property";
298314

315+
public final int id = 1;
316+
299317
}
300318

301319
public static class CopyUserPrincipal {

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

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -68,6 +68,8 @@ public class AuthenticationPrincipalArgumentResolverTests {
6868

6969
ResolvableMethod spel = ResolvableMethod.on(getClass()).named("spel").build();
7070

71+
ResolvableMethod spelPrimitive = ResolvableMethod.on(getClass()).named("spelPrimitive").build();
72+
7173
ResolvableMethod meta = ResolvableMethod.on(getClass()).named("meta").build();
7274

7375
ResolvableMethod bean = ResolvableMethod.on(getClass()).named("bean").build();
@@ -136,6 +138,16 @@ public void resolveArgumentWhenSpelThenObtainsPrincipal() {
136138
assertThat(argument.block()).isEqualTo(user.getId());
137139
}
138140

141+
@Test
142+
public void resolveArgumentWhenSpelWithPrimitiveThenObtainsPrincipal() {
143+
MyUserPrimitive user = new MyUserPrimitive(3);
144+
MethodParameter parameter = this.spelPrimitive.arg(int.class);
145+
given(this.authentication.getPrincipal()).willReturn(user);
146+
Mono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange)
147+
.subscriberContext(ReactiveSecurityContextHolder.withAuthentication(this.authentication));
148+
assertThat(argument.block()).isEqualTo(user.getId());
149+
}
150+
139151
@Test
140152
public void resolveArgumentWhenBeanThenObtainsPrincipal() throws Exception {
141153
MyUser user = new MyUser(3L);
@@ -196,6 +208,9 @@ void authenticationPrincipalNoGeneric(@AuthenticationPrincipal Mono monoPrincipa
196208
void spel(@AuthenticationPrincipal(expression = "id") Long id) {
197209
}
198210

211+
void spelPrimitive(@AuthenticationPrincipal(expression = "id") int id) {
212+
}
213+
199214
void bean(@AuthenticationPrincipal(expression = "@beanName.methodName(#this)") Long id) {
200215
}
201216

@@ -233,6 +248,20 @@ public Long getId() {
233248

234249
}
235250

251+
static class MyUserPrimitive {
252+
253+
private final int id;
254+
255+
MyUserPrimitive(int id) {
256+
this.id = id;
257+
}
258+
259+
public int getId() {
260+
return this.id;
261+
}
262+
263+
}
264+
236265
@Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
237266
@Retention(RetentionPolicy.RUNTIME)
238267
@Documented

0 commit comments

Comments
 (0)