瀏覽代碼

Allow AuthenticationPrincipal argument type to be primitive

Closes gh-10172
Eleftheria Stein 4 年之前
父節點
當前提交
7d81a52780

+ 3 - 2
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());
 			}

+ 3 - 2
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());
 	}
 
 	/**

+ 18 - 1
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 {

+ 23 - 1
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<String> username) {
 	}
 
+	@Test
+	public void resolveArgumentWhenExpressionPrimitiveThenFound() {
+		CustomUserPrincipal principal = new CustomUserPrincipal();
+		// @formatter:off
+		Mono<Object> 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;
+
+	}
+
 }

+ 3 - 2
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());
 			}

+ 3 - 2
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());
 	}
 
 	/**

+ 19 - 1
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 {

+ 30 - 1
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<Object> 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