diff --git a/messaging/src/main/java/org/springframework/security/messaging/context/AuthenticationPrincipalArgumentResolver.java b/messaging/src/main/java/org/springframework/security/messaging/context/AuthenticationPrincipalArgumentResolver.java index 58cd4f720e..aaf99c16ee 100644 --- a/messaging/src/main/java/org/springframework/security/messaging/context/AuthenticationPrincipalArgumentResolver.java +++ b/messaging/src/main/java/org/springframework/security/messaging/context/AuthenticationPrincipalArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Controller; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** @@ -107,7 +108,7 @@ public final class AuthenticationPrincipalArgumentResolver implements HandlerMet Expression expression = this.parser.parseExpression(expressionToParse); principal = expression.getValue(context); } - if (principal != null && !parameter.getParameterType().isAssignableFrom(principal.getClass())) { + if (principal != null && !ClassUtils.isAssignable(parameter.getParameterType(), principal.getClass())) { if (authPrincipal.errorOnInvalidType()) { throw new ClassCastException(principal + " is not assignable to " + parameter.getParameterType()); } diff --git a/messaging/src/main/java/org/springframework/security/messaging/handler/invocation/reactive/AuthenticationPrincipalArgumentResolver.java b/messaging/src/main/java/org/springframework/security/messaging/handler/invocation/reactive/AuthenticationPrincipalArgumentResolver.java index 2c83e97902..f6baa3a328 100644 --- a/messaging/src/main/java/org/springframework/security/messaging/handler/invocation/reactive/AuthenticationPrincipalArgumentResolver.java +++ b/messaging/src/main/java/org/springframework/security/messaging/handler/invocation/reactive/AuthenticationPrincipalArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 the original author or authors. + * Copyright 2019-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,6 +39,7 @@ import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.SecurityContext; import org.springframework.stereotype.Controller; import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** @@ -170,7 +171,7 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg } typeToCheck = genericType; } - return !typeToCheck.isAssignableFrom(principal.getClass()); + return !ClassUtils.isAssignable(typeToCheck, principal.getClass()); } /** diff --git a/messaging/src/test/java/org/springframework/security/messaging/context/AuthenticationPrincipalArgumentResolverTests.java b/messaging/src/test/java/org/springframework/security/messaging/context/AuthenticationPrincipalArgumentResolverTests.java index 7f0148bc12..2824a6b981 100644 --- a/messaging/src/test/java/org/springframework/security/messaging/context/AuthenticationPrincipalArgumentResolverTests.java +++ b/messaging/src/test/java/org/springframework/security/messaging/context/AuthenticationPrincipalArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -133,6 +133,14 @@ public class AuthenticationPrincipalArgumentResolverTests { assertThat(resolveArgument).isNotSameAs(principal); } + @Test + public void resolveArgumentSpelPrimitive() throws Exception { + CustomUserPrincipal principal = new CustomUserPrincipal(); + setAuthenticationPrincipal(principal); + this.expectedPrincipal = principal.id; + assertThat(this.resolver.resolveArgument(showUserSpelPrimitive(), null)).isEqualTo(this.expectedPrincipal); + } + @Test public void resolveArgumentNullOnInvalidType() throws Exception { setAuthenticationPrincipal(new CustomUserPrincipal()); @@ -195,6 +203,10 @@ public class AuthenticationPrincipalArgumentResolverTests { return getMethodParameter("showUserSpelCopy", CopyUserPrincipal.class); } + private MethodParameter showUserSpelPrimitive() { + return getMethodParameter("showUserSpelPrimitive", int.class); + } + private MethodParameter showUserAnnotationObject() { return getMethodParameter("showUserAnnotation", Object.class); } @@ -258,12 +270,17 @@ public class AuthenticationPrincipalArgumentResolverTests { expression = "new org.springframework.security.messaging.context.AuthenticationPrincipalArgumentResolverTests$CopyUserPrincipal(#this)") CopyUserPrincipal user) { } + public void showUserSpelPrimitive(@AuthenticationPrincipal(expression = "id") int id) { + } + } static class CustomUserPrincipal { public final String property = "property"; + public final int id = 1; + } public static class CopyUserPrincipal { diff --git a/messaging/src/test/java/org/springframework/security/messaging/handler/invocation/reactive/AuthenticationPrincipalArgumentResolverTests.java b/messaging/src/test/java/org/springframework/security/messaging/handler/invocation/reactive/AuthenticationPrincipalArgumentResolverTests.java index 818e2cf351..b9e2786c22 100644 --- a/messaging/src/test/java/org/springframework/security/messaging/handler/invocation/reactive/AuthenticationPrincipalArgumentResolverTests.java +++ b/messaging/src/test/java/org/springframework/security/messaging/handler/invocation/reactive/AuthenticationPrincipalArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 the original author or authors. + * Copyright 2019-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import reactor.core.publisher.Mono; import org.springframework.core.MethodParameter; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.security.authentication.TestAuthentication; +import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.context.ReactiveSecurityContextHolder; @@ -105,6 +106,21 @@ public class AuthenticationPrincipalArgumentResolverTests { @AuthenticationPrincipal(expression = "username") Mono username) { } + @Test + public void resolveArgumentWhenExpressionPrimitiveThenFound() { + CustomUserPrincipal principal = new CustomUserPrincipal(); + // @formatter:off + Mono result = this.resolver + .resolveArgument(arg0("authenticationPrincipalExpressionPrimitive"), null) + .subscriberContext(ReactiveSecurityContextHolder.withAuthentication(new TestingAuthenticationToken(principal, "password", "ROLE_USER"))); + // @formatter:on + assertThat(result.block()).isEqualTo(principal.id); + } + + @SuppressWarnings("unused") + private void authenticationPrincipalExpressionPrimitive(@AuthenticationPrincipal(expression = "id") int username) { + } + @Test public void supportsParameterWhenNotAnnotatedThenFalse() { assertThat(this.resolver.supportsParameter(arg0("monoUserDetails"))).isFalse(); @@ -125,4 +141,10 @@ public class AuthenticationPrincipalArgumentResolverTests { } + static class CustomUserPrincipal { + + public final int id = 1; + + } + } diff --git a/web/src/main/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolver.java b/web/src/main/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolver.java index c54a81d3b8..3f0baa6ebb 100644 --- a/web/src/main/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolver.java +++ b/web/src/main/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Controller; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; @@ -114,7 +115,7 @@ public final class AuthenticationPrincipalArgumentResolver implements HandlerMet Expression expression = this.parser.parseExpression(expressionToParse); principal = expression.getValue(context); } - if (principal != null && !parameter.getParameterType().isAssignableFrom(principal.getClass())) { + if (principal != null && !ClassUtils.isAssignable(parameter.getParameterType(), principal.getClass())) { if (annotation.errorOnInvalidType()) { throw new ClassCastException(principal + " is not assignable to " + parameter.getParameterType()); } diff --git a/web/src/main/java/org/springframework/security/web/reactive/result/method/annotation/AuthenticationPrincipalArgumentResolver.java b/web/src/main/java/org/springframework/security/web/reactive/result/method/annotation/AuthenticationPrincipalArgumentResolver.java index 5be69521ab..58c0db8e74 100644 --- a/web/src/main/java/org/springframework/security/web/reactive/result/method/annotation/AuthenticationPrincipalArgumentResolver.java +++ b/web/src/main/java/org/springframework/security/web/reactive/result/method/annotation/AuthenticationPrincipalArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.SecurityContext; +import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import org.springframework.web.reactive.BindingContext; import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverSupport; @@ -114,7 +115,7 @@ public class AuthenticationPrincipalArgumentResolver extends HandlerMethodArgume } typeToCheck = genericType; } - return !typeToCheck.isAssignableFrom(principal.getClass()); + return !ClassUtils.isAssignable(typeToCheck, principal.getClass()); } /** diff --git a/web/src/test/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolverTests.java b/web/src/test/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolverTests.java index c995cdcda4..daa15736bb 100644 --- a/web/src/test/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolverTests.java +++ b/web/src/test/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -157,6 +157,15 @@ public class AuthenticationPrincipalArgumentResolverTests { assertThat(resolveArgument).isNotSameAs(principal); } + @Test + public void resolveArgumentSpelPrimitive() throws Exception { + CustomUserPrincipal principal = new CustomUserPrincipal(); + setAuthenticationPrincipal(principal); + this.expectedPrincipal = principal.id; + assertThat(this.resolver.resolveArgument(showUserSpelPrimitive(), null, null, null)) + .isEqualTo(this.expectedPrincipal); + } + @Test public void resolveArgumentNullOnInvalidType() throws Exception { setAuthenticationPrincipal(new CustomUserPrincipal()); @@ -224,6 +233,10 @@ public class AuthenticationPrincipalArgumentResolverTests { return getMethodParameter("showUserSpelCopy", CopyUserPrincipal.class); } + private MethodParameter showUserSpelPrimitive() { + return getMethodParameter("showUserSpelPrimitive", int.class); + } + private MethodParameter showUserAnnotationObject() { return getMethodParameter("showUserAnnotation", Object.class); } @@ -290,12 +303,17 @@ public class AuthenticationPrincipalArgumentResolverTests { expression = "new org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolverTests$CopyUserPrincipal(#this)") CopyUserPrincipal user) { } + public void showUserSpelPrimitive(@AuthenticationPrincipal(expression = "id") int id) { + } + } static class CustomUserPrincipal { public final String property = "property"; + public final int id = 1; + } public static class CopyUserPrincipal { diff --git a/web/src/test/java/org/springframework/security/web/reactive/result/method/annotation/AuthenticationPrincipalArgumentResolverTests.java b/web/src/test/java/org/springframework/security/web/reactive/result/method/annotation/AuthenticationPrincipalArgumentResolverTests.java index f86c3c6281..6b52dc857b 100644 --- a/web/src/test/java/org/springframework/security/web/reactive/result/method/annotation/AuthenticationPrincipalArgumentResolverTests.java +++ b/web/src/test/java/org/springframework/security/web/reactive/result/method/annotation/AuthenticationPrincipalArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,6 +68,8 @@ public class AuthenticationPrincipalArgumentResolverTests { ResolvableMethod spel = ResolvableMethod.on(getClass()).named("spel").build(); + ResolvableMethod spelPrimitive = ResolvableMethod.on(getClass()).named("spelPrimitive").build(); + ResolvableMethod meta = ResolvableMethod.on(getClass()).named("meta").build(); ResolvableMethod bean = ResolvableMethod.on(getClass()).named("bean").build(); @@ -136,6 +138,16 @@ public class AuthenticationPrincipalArgumentResolverTests { assertThat(argument.block()).isEqualTo(user.getId()); } + @Test + public void resolveArgumentWhenSpelWithPrimitiveThenObtainsPrincipal() { + MyUserPrimitive user = new MyUserPrimitive(3); + MethodParameter parameter = this.spelPrimitive.arg(int.class); + given(this.authentication.getPrincipal()).willReturn(user); + Mono argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange) + .subscriberContext(ReactiveSecurityContextHolder.withAuthentication(this.authentication)); + assertThat(argument.block()).isEqualTo(user.getId()); + } + @Test public void resolveArgumentWhenBeanThenObtainsPrincipal() throws Exception { MyUser user = new MyUser(3L); @@ -196,6 +208,9 @@ public class AuthenticationPrincipalArgumentResolverTests { void spel(@AuthenticationPrincipal(expression = "id") Long id) { } + void spelPrimitive(@AuthenticationPrincipal(expression = "id") int id) { + } + void bean(@AuthenticationPrincipal(expression = "@beanName.methodName(#this)") Long id) { } @@ -233,6 +248,20 @@ public class AuthenticationPrincipalArgumentResolverTests { } + static class MyUserPrimitive { + + private final int id; + + MyUserPrimitive(int id) { + this.id = id; + } + + public int getId() { + return this.id; + } + + } + @Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented