Explorar el Código

Improve @CurrentSecurityContext meta-annotations

Closes gh-15551
DingHao hace 1 año
padre
commit
ed16c86115

+ 1 - 0
config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration.java

@@ -98,6 +98,7 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
 		CurrentSecurityContextArgumentResolver currentSecurityContextArgumentResolver = new CurrentSecurityContextArgumentResolver();
 		currentSecurityContextArgumentResolver.setBeanResolver(this.beanResolver);
 		currentSecurityContextArgumentResolver.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
+		currentSecurityContextArgumentResolver.setTemplateDefaults(this.templateDefaults);
 		argumentResolvers.add(currentSecurityContextArgumentResolver);
 		argumentResolvers.add(new CsrfTokenArgumentResolver());
 	}

+ 7 - 3
config/src/main/java/org/springframework/security/config/annotation/web/reactive/ServerHttpSecurityConfiguration.java

@@ -109,12 +109,14 @@ class ServerHttpSecurityConfiguration {
 
 	@Bean
 	static WebFluxConfigurer authenticationPrincipalArgumentResolverConfigurer(
-			ObjectProvider<AuthenticationPrincipalArgumentResolver> authenticationPrincipalArgumentResolver) {
+			ObjectProvider<AuthenticationPrincipalArgumentResolver> authenticationPrincipalArgumentResolver,
+			ObjectProvider<CurrentSecurityContextArgumentResolver> currentSecurityContextArgumentResolvers) {
 		return new WebFluxConfigurer() {
 
 			@Override
 			public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
-				configurer.addCustomResolver(authenticationPrincipalArgumentResolver.getObject());
+				configurer.addCustomResolver(authenticationPrincipalArgumentResolver.getObject(),
+						currentSecurityContextArgumentResolvers.getObject());
 			}
 
 		};
@@ -133,12 +135,14 @@ class ServerHttpSecurityConfiguration {
 	}
 
 	@Bean
-	CurrentSecurityContextArgumentResolver reactiveCurrentSecurityContextArgumentResolver() {
+	CurrentSecurityContextArgumentResolver reactiveCurrentSecurityContextArgumentResolver(
+			ObjectProvider<AnnotationTemplateExpressionDefaults> templateDefaults) {
 		CurrentSecurityContextArgumentResolver resolver = new CurrentSecurityContextArgumentResolver(
 				this.adapterRegistry);
 		if (this.beanFactory != null) {
 			resolver.setBeanResolver(new BeanFactoryResolver(this.beanFactory));
 		}
+		templateDefaults.ifAvailable(resolver::setTemplateDefaults);
 		return resolver;
 	}
 

+ 26 - 0
config/src/test/java/org/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfigurationTests.java

@@ -33,6 +33,7 @@ import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.security.core.annotation.CurrentSecurityContext;
 import org.springframework.security.core.authority.AuthorityUtils;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.web.csrf.CsrfToken;
@@ -115,6 +116,15 @@ public class WebMvcSecurityConfigurationTests {
 		this.mockMvc.perform(get("/hi")).andExpect(content().string("Hi, Harold!"));
 	}
 
+	@Test
+	public void resolveMetaAnnotationWhenTemplateDefaultsBeanThenResolvesExpression() throws Exception {
+		this.mockMvc.perform(get("/hello")).andExpect(content().string("user"));
+		Authentication harold = new TestingAuthenticationToken("harold", "password",
+				AuthorityUtils.createAuthorityList("ROLE_USER"));
+		SecurityContextHolder.getContext().setAuthentication(harold);
+		this.mockMvc.perform(get("/hello")).andExpect(content().string("harold"));
+	}
+
 	private ResultMatcher assertResult(Object expected) {
 		return model().attribute("result", expected);
 	}
@@ -128,6 +138,15 @@ public class WebMvcSecurityConfigurationTests {
 
 	}
 
+	@Target({ ElementType.PARAMETER })
+	@Retention(RetentionPolicy.RUNTIME)
+	@CurrentSecurityContext(expression = "authentication.{property}")
+	@interface CurrentAuthenticationProperty {
+
+		String property();
+
+	}
+
 	@Controller
 	static class TestController {
 
@@ -158,6 +177,13 @@ public class WebMvcSecurityConfigurationTests {
 			}
 		}
 
+		@GetMapping("/hello")
+		@ResponseBody
+		String getCurrentAuthenticationProperty(
+				@CurrentAuthenticationProperty(property = "principal") String principal) {
+			return principal;
+		}
+
 	}
 
 	@Configuration

+ 37 - 0
config/src/test/java/org/springframework/security/config/annotation/web/reactive/ServerHttpSecurityConfigurationTests.java

@@ -42,6 +42,7 @@ import org.springframework.security.config.web.server.ServerHttpSecurity;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
+import org.springframework.security.core.annotation.CurrentSecurityContext;
 import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
 import org.springframework.security.core.userdetails.PasswordEncodedUser;
 import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
@@ -183,6 +184,27 @@ public class ServerHttpSecurityConfigurationTests {
 			.isEqualTo("Hi, Harold!");
 	}
 
+	@Test
+	public void resoleMetaAnnotationWhenTemplateDefaultsBeanThenResolvesExpression() throws Exception {
+		this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire();
+		Authentication user = new TestingAuthenticationToken("user", "password", "ROLE_USER");
+		this.webClient.mutateWith(mockAuthentication(user))
+			.get()
+			.uri("/hello")
+			.exchange()
+			.expectStatus()
+			.isOk()
+			.expectBody(String.class)
+			.isEqualTo("user");
+		Authentication harold = new TestingAuthenticationToken("harold", "password", "ROLE_USER");
+		this.webClient.mutateWith(mockAuthentication(harold))
+			.get()
+			.uri("/hello")
+			.exchange()
+			.expectBody(String.class)
+			.isEqualTo("harold");
+	}
+
 	@Configuration
 	static class SubclassConfig extends ServerHttpSecurityConfiguration {
 
@@ -283,6 +305,15 @@ public class ServerHttpSecurityConfigurationTests {
 
 	}
 
+	@Target({ ElementType.PARAMETER })
+	@Retention(RetentionPolicy.RUNTIME)
+	@CurrentSecurityContext(expression = "authentication.{property}")
+	@interface CurrentAuthenticationProperty {
+
+		String property();
+
+	}
+
 	@RestController
 	static class TestController {
 
@@ -296,6 +327,12 @@ public class ServerHttpSecurityConfigurationTests {
 			}
 		}
 
+		@GetMapping("/hello")
+		String getCurrentAuthenticationProperty(
+				@CurrentAuthenticationProperty(property = "principal") String principal) {
+			return principal;
+		}
+
 	}
 
 	@Configuration

+ 30 - 19
messaging/src/main/java/org/springframework/security/messaging/handler/invocation/reactive/CurrentSecurityContextArgumentResolver.java

@@ -17,6 +17,8 @@
 package org.springframework.security.messaging.handler.invocation.reactive;
 
 import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.reactivestreams.Publisher;
 import reactor.core.publisher.Mono;
@@ -25,7 +27,6 @@ import org.springframework.core.MethodParameter;
 import org.springframework.core.ReactiveAdapter;
 import org.springframework.core.ReactiveAdapterRegistry;
 import org.springframework.core.ResolvableType;
-import org.springframework.core.annotation.AnnotationUtils;
 import org.springframework.expression.BeanResolver;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ExpressionParser;
@@ -34,6 +35,9 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
 import org.springframework.messaging.Message;
 import org.springframework.messaging.handler.invocation.reactive.HandlerMethodArgumentResolver;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.annotation.AnnotationSynthesizer;
+import org.springframework.security.core.annotation.AnnotationSynthesizers;
+import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
 import org.springframework.security.core.annotation.CurrentSecurityContext;
 import org.springframework.security.core.context.ReactiveSecurityContextHolder;
 import org.springframework.security.core.context.SecurityContext;
@@ -88,12 +92,18 @@ import org.springframework.util.StringUtils;
  * </pre>
  *
  * @author Rob Winch
+ * @author DingHao
  * @since 5.2
  */
 public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgumentResolver {
 
+	private final Map<MethodParameter, Annotation> cachedAttributes = new ConcurrentHashMap<>();
+
 	private ExpressionParser parser = new SpelExpressionParser();
 
+	private AnnotationSynthesizer<CurrentSecurityContext> synthesizer = AnnotationSynthesizers
+		.requireUnique(CurrentSecurityContext.class);
+
 	private BeanResolver beanResolver;
 
 	private ReactiveAdapterRegistry adapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
@@ -118,8 +128,7 @@ public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgu
 
 	@Override
 	public boolean supportsParameter(MethodParameter parameter) {
-		return isMonoSecurityContext(parameter)
-				|| findMethodAnnotation(CurrentSecurityContext.class, parameter) != null;
+		return isMonoSecurityContext(parameter) || findMethodAnnotation(parameter) != null;
 	}
 
 	private boolean isMonoSecurityContext(MethodParameter parameter) {
@@ -149,7 +158,7 @@ public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgu
 	}
 
 	private Object resolveSecurityContext(MethodParameter parameter, Object securityContext) {
-		CurrentSecurityContext contextAnno = findMethodAnnotation(CurrentSecurityContext.class, parameter);
+		CurrentSecurityContext contextAnno = findMethodAnnotation(parameter);
 		if (contextAnno != null) {
 			return resolveSecurityContextFromAnnotation(contextAnno, parameter, securityContext);
 		}
@@ -193,26 +202,28 @@ public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgu
 		return !typeToCheck.isAssignableFrom(value.getClass());
 	}
 
+	/**
+	 * Configure CurrentSecurityContext template resolution
+	 * <p>
+	 * By default, this value is <code>null</code>, which indicates that templates should
+	 * not be resolved.
+	 * @param templateDefaults - whether to resolve CurrentSecurityContext templates
+	 * parameters
+	 * @since 6.4
+	 */
+	public void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {
+		this.synthesizer = AnnotationSynthesizers.requireUnique(CurrentSecurityContext.class, templateDefaults);
+	}
+
 	/**
 	 * Obtains the specified {@link Annotation} on the specified {@link MethodParameter}.
-	 * @param annotationClass the class of the {@link Annotation} to find on the
-	 * {@link MethodParameter}
 	 * @param parameter the {@link MethodParameter} to search for an {@link Annotation}
 	 * @return the {@link Annotation} that was found or null.
 	 */
-	private <T extends Annotation> T findMethodAnnotation(Class<T> annotationClass, MethodParameter parameter) {
-		T annotation = parameter.getParameterAnnotation(annotationClass);
-		if (annotation != null) {
-			return annotation;
-		}
-		Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
-		for (Annotation toSearch : annotationsToSearch) {
-			annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass);
-			if (annotation != null) {
-				return annotation;
-			}
-		}
-		return null;
+	@SuppressWarnings("unchecked")
+	private <T extends Annotation> T findMethodAnnotation(MethodParameter parameter) {
+		return (T) this.cachedAttributes.computeIfAbsent(parameter,
+				(methodParameter) -> this.synthesizer.synthesize(methodParameter.getParameter()));
 	}
 
 }

+ 56 - 0
messaging/src/test/java/org/springframework/security/messaging/handler/invocation/reactive/CurrentSecurityContextArgumentResolverTests.java

@@ -16,16 +16,20 @@
 
 package org.springframework.security.messaging.handler.invocation.reactive;
 
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 import org.junit.jupiter.api.Test;
 import reactor.core.publisher.Mono;
 
 import org.springframework.core.MethodParameter;
+import org.springframework.core.annotation.AliasFor;
 import org.springframework.core.annotation.SynthesizingMethodParameter;
 import org.springframework.security.authentication.TestAuthentication;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
 import org.springframework.security.core.annotation.CurrentSecurityContext;
 import org.springframework.security.core.context.ReactiveSecurityContextHolder;
 import org.springframework.security.core.context.SecurityContext;
@@ -171,6 +175,39 @@ public class CurrentSecurityContextArgumentResolverTests {
 		assertThat(result.block().getAuthentication().getPrincipal()).isEqualTo(authentication.getPrincipal());
 	}
 
+	@Test
+	public void resolveArgumentCustomMetaAnnotation() {
+		Authentication authentication = TestAuthentication.authenticatedUser();
+		CustomSecurityContext securityContext = new CustomSecurityContext();
+		securityContext.setAuthentication(authentication);
+		Mono<UserDetails> result = (Mono<UserDetails>) this.resolver
+			.resolveArgument(arg0("showUserCustomMetaAnnotation"), null)
+			.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))
+			.block();
+		assertThat(result.block()).isEqualTo(authentication.getPrincipal());
+	}
+
+	@Test
+	public void resolveArgumentCustomMetaAnnotationTpl() {
+		this.resolver.setTemplateDefaults(new AnnotationTemplateExpressionDefaults());
+		Authentication authentication = TestAuthentication.authenticatedUser();
+		CustomSecurityContext securityContext = new CustomSecurityContext();
+		securityContext.setAuthentication(authentication);
+		Mono<UserDetails> result = (Mono<UserDetails>) this.resolver
+			.resolveArgument(arg0("showUserCustomMetaAnnotationTpl"), null)
+			.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))
+			.block();
+		assertThat(result.block()).isEqualTo(authentication.getPrincipal());
+	}
+
+	private void showUserCustomMetaAnnotation(
+			@AliasedCurrentSecurityContext(expression = "authentication.principal") Mono<UserDetails> user) {
+	}
+
+	private void showUserCustomMetaAnnotationTpl(
+			@CurrentAuthenticationProperty(property = "principal") Mono<UserDetails> user) {
+	}
+
 	@SuppressWarnings("unused")
 	private void monoCustomSecurityContext(Mono<CustomSecurityContext> securityContext) {
 	}
@@ -186,6 +223,25 @@ public class CurrentSecurityContextArgumentResolverTests {
 
 	}
 
+	@Target({ ElementType.PARAMETER })
+	@Retention(RetentionPolicy.RUNTIME)
+	@CurrentSecurityContext
+	@interface AliasedCurrentSecurityContext {
+
+		@AliasFor(annotation = CurrentSecurityContext.class)
+		String expression() default "";
+
+	}
+
+	@Target({ ElementType.PARAMETER })
+	@Retention(RetentionPolicy.RUNTIME)
+	@CurrentSecurityContext(expression = "authentication.{property}")
+	@interface CurrentAuthenticationProperty {
+
+		String property() default "";
+
+	}
+
 	static class CustomSecurityContext implements SecurityContext {
 
 		private Authentication authentication;

+ 31 - 19
web/src/main/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolver.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2024 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.
@@ -17,14 +17,18 @@
 package org.springframework.security.web.method.annotation;
 
 import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.springframework.core.MethodParameter;
-import org.springframework.core.annotation.AnnotationUtils;
 import org.springframework.expression.BeanResolver;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ExpressionParser;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
 import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.security.core.annotation.AnnotationSynthesizer;
+import org.springframework.security.core.annotation.AnnotationSynthesizers;
+import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
 import org.springframework.security.core.annotation.CurrentSecurityContext;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -72,6 +76,7 @@ import org.springframework.web.method.support.ModelAndViewContainer;
  * </p>
  *
  * @author Dan Zheng
+ * @author DingHao
  * @since 5.2
  */
 public final class CurrentSecurityContextArgumentResolver implements HandlerMethodArgumentResolver {
@@ -79,14 +84,19 @@ public final class CurrentSecurityContextArgumentResolver implements HandlerMeth
 	private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
 		.getContextHolderStrategy();
 
+	private final Map<MethodParameter, Annotation> cachedAttributes = new ConcurrentHashMap<>();
+
 	private ExpressionParser parser = new SpelExpressionParser();
 
+	private AnnotationSynthesizer<CurrentSecurityContext> synthesizer = AnnotationSynthesizers
+		.requireUnique(CurrentSecurityContext.class);
+
 	private BeanResolver beanResolver;
 
 	@Override
 	public boolean supportsParameter(MethodParameter parameter) {
 		return SecurityContext.class.isAssignableFrom(parameter.getParameterType())
-				|| findMethodAnnotation(CurrentSecurityContext.class, parameter) != null;
+				|| findMethodAnnotation(parameter) != null;
 	}
 
 	@Override
@@ -96,7 +106,7 @@ public final class CurrentSecurityContextArgumentResolver implements HandlerMeth
 		if (securityContext == null) {
 			return null;
 		}
-		CurrentSecurityContext annotation = findMethodAnnotation(CurrentSecurityContext.class, parameter);
+		CurrentSecurityContext annotation = findMethodAnnotation(parameter);
 		if (annotation != null) {
 			return resolveSecurityContextFromAnnotation(parameter, annotation, securityContext);
 		}
@@ -124,6 +134,19 @@ public final class CurrentSecurityContextArgumentResolver implements HandlerMeth
 		this.beanResolver = beanResolver;
 	}
 
+	/**
+	 * Configure CurrentSecurityContext template resolution
+	 * <p>
+	 * By default, this value is <code>null</code>, which indicates that templates should
+	 * not be resolved.
+	 * @param templateDefaults - whether to resolve CurrentSecurityContext templates
+	 * parameters
+	 * @since 6.4
+	 */
+	public void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {
+		this.synthesizer = AnnotationSynthesizers.requireUnique(CurrentSecurityContext.class, templateDefaults);
+	}
+
 	private Object resolveSecurityContextFromAnnotation(MethodParameter parameter, CurrentSecurityContext annotation,
 			SecurityContext securityContext) {
 		Object securityContextResult = securityContext;
@@ -149,24 +172,13 @@ public final class CurrentSecurityContextArgumentResolver implements HandlerMeth
 
 	/**
 	 * Obtain the specified {@link Annotation} on the specified {@link MethodParameter}.
-	 * @param annotationClass the class of the {@link Annotation} to find on the
-	 * {@link MethodParameter}
 	 * @param parameter the {@link MethodParameter} to search for an {@link Annotation}
 	 * @return the {@link Annotation} that was found or null.
 	 */
-	private <T extends Annotation> T findMethodAnnotation(Class<T> annotationClass, MethodParameter parameter) {
-		T annotation = parameter.getParameterAnnotation(annotationClass);
-		if (annotation != null) {
-			return annotation;
-		}
-		Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
-		for (Annotation toSearch : annotationsToSearch) {
-			annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass);
-			if (annotation != null) {
-				return annotation;
-			}
-		}
-		return null;
+	@SuppressWarnings("unchecked")
+	private <T extends Annotation> T findMethodAnnotation(MethodParameter parameter) {
+		return (T) this.cachedAttributes.computeIfAbsent(parameter,
+				(methodParameter) -> this.synthesizer.synthesize(methodParameter.getParameter()));
 	}
 
 }

+ 31 - 20
web/src/main/java/org/springframework/security/web/reactive/result/method/annotation/CurrentSecurityContextArgumentResolver.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2024 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.
@@ -17,6 +17,8 @@
 package org.springframework.security.web.reactive.result.method.annotation;
 
 import java.lang.annotation.Annotation;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.reactivestreams.Publisher;
 import reactor.core.publisher.Mono;
@@ -25,12 +27,14 @@ import org.springframework.core.MethodParameter;
 import org.springframework.core.ReactiveAdapter;
 import org.springframework.core.ReactiveAdapterRegistry;
 import org.springframework.core.ResolvableType;
-import org.springframework.core.annotation.AnnotationUtils;
 import org.springframework.expression.BeanResolver;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ExpressionParser;
 import org.springframework.expression.spel.standard.SpelExpressionParser;
 import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.security.core.annotation.AnnotationSynthesizer;
+import org.springframework.security.core.annotation.AnnotationSynthesizers;
+import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
 import org.springframework.security.core.annotation.CurrentSecurityContext;
 import org.springframework.security.core.context.ReactiveSecurityContextHolder;
 import org.springframework.security.core.context.SecurityContext;
@@ -44,12 +48,18 @@ import org.springframework.web.server.ServerWebExchange;
  * Resolves the {@link SecurityContext}
  *
  * @author Dan Zheng
+ * @author DingHao
  * @since 5.2
  */
 public class CurrentSecurityContextArgumentResolver extends HandlerMethodArgumentResolverSupport {
 
+	private final Map<MethodParameter, Annotation> cachedAttributes = new ConcurrentHashMap<>();
+
 	private ExpressionParser parser = new SpelExpressionParser();
 
+	private AnnotationSynthesizer<CurrentSecurityContext> synthesizer = AnnotationSynthesizers
+		.requireUnique(CurrentSecurityContext.class);
+
 	private BeanResolver beanResolver;
 
 	public CurrentSecurityContextArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {
@@ -65,10 +75,22 @@ public class CurrentSecurityContextArgumentResolver extends HandlerMethodArgumen
 		this.beanResolver = beanResolver;
 	}
 
+	/**
+	 * Configure CurrentSecurityContext template resolution
+	 * <p>
+	 * By default, this value is <code>null</code>, which indicates that templates should
+	 * not be resolved.
+	 * @param templateDefaults - whether to resolve CurrentSecurityContext templates
+	 * parameters
+	 * @since 6.4
+	 */
+	public void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {
+		this.synthesizer = AnnotationSynthesizers.requireUnique(CurrentSecurityContext.class, templateDefaults);
+	}
+
 	@Override
 	public boolean supportsParameter(MethodParameter parameter) {
-		return isMonoSecurityContext(parameter)
-				|| findMethodAnnotation(CurrentSecurityContext.class, parameter) != null;
+		return isMonoSecurityContext(parameter) || findMethodAnnotation(parameter) != null;
 	}
 
 	private boolean isMonoSecurityContext(MethodParameter parameter) {
@@ -108,7 +130,7 @@ public class CurrentSecurityContextArgumentResolver extends HandlerMethodArgumen
 	 * @return the resolved object from expression.
 	 */
 	private Object resolveSecurityContext(MethodParameter parameter, SecurityContext securityContext) {
-		CurrentSecurityContext annotation = findMethodAnnotation(CurrentSecurityContext.class, parameter);
+		CurrentSecurityContext annotation = findMethodAnnotation(parameter);
 		if (annotation != null) {
 			return resolveSecurityContextFromAnnotation(annotation, parameter, securityContext);
 		}
@@ -162,24 +184,13 @@ public class CurrentSecurityContextArgumentResolver extends HandlerMethodArgumen
 
 	/**
 	 * Obtains the specified {@link Annotation} on the specified {@link MethodParameter}.
-	 * @param annotationClass the class of the {@link Annotation} to find on the
-	 * {@link MethodParameter}
 	 * @param parameter the {@link MethodParameter} to search for an {@link Annotation}
 	 * @return the {@link Annotation} that was found or null.
 	 */
-	private <T extends Annotation> T findMethodAnnotation(Class<T> annotationClass, MethodParameter parameter) {
-		T annotation = parameter.getParameterAnnotation(annotationClass);
-		if (annotation != null) {
-			return annotation;
-		}
-		Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
-		for (Annotation toSearch : annotationsToSearch) {
-			annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass);
-			if (annotation != null) {
-				return annotation;
-			}
-		}
-		return null;
+	@SuppressWarnings("unchecked")
+	private <T extends Annotation> T findMethodAnnotation(MethodParameter parameter) {
+		return (T) this.cachedAttributes.computeIfAbsent(parameter,
+				(methodParameter) -> this.synthesizer.synthesize(methodParameter.getParameter()));
 	}
 
 }

+ 55 - 1
web/src/test/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolverTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2024 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.
@@ -27,10 +27,12 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import org.springframework.core.MethodParameter;
+import org.springframework.core.annotation.AliasFor;
 import org.springframework.expression.BeanResolver;
 import org.springframework.expression.spel.SpelEvaluationException;
 import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
 import org.springframework.security.core.annotation.CurrentSecurityContext;
 import org.springframework.security.core.authority.AuthorityUtils;
 import org.springframework.security.core.context.SecurityContext;
@@ -247,6 +249,23 @@ public class CurrentSecurityContextArgumentResolverTests {
 			.resolveArgument(showCurrentSecurityWithErrorOnInvalidTypeMisMatch(), null, null, null));
 	}
 
+	@Test
+	public void resolveArgumentCustomMetaAnnotation() {
+		String principal = "current_authentcation";
+		setAuthenticationPrincipal(principal);
+		String p = (String) this.resolver.resolveArgument(showUserCustomMetaAnnotation(), null, null, null);
+		assertThat(p).isEqualTo(principal);
+	}
+
+	@Test
+	public void resolveArgumentCustomMetaAnnotationTpl() {
+		String principal = "current_authentcation";
+		setAuthenticationPrincipal(principal);
+		this.resolver.setTemplateDefaults(new AnnotationTemplateExpressionDefaults());
+		String p = (String) this.resolver.resolveArgument(showUserCustomMetaAnnotationTpl(), null, null, null);
+		assertThat(p).isEqualTo(principal);
+	}
+
 	private MethodParameter showSecurityContextNoAnnotationTypeMismatch() {
 		return getMethodParameter("showSecurityContextNoAnnotation", String.class);
 	}
@@ -307,6 +326,14 @@ public class CurrentSecurityContextArgumentResolverTests {
 		return getMethodParameter("showCurrentAuthentication", Authentication.class);
 	}
 
+	public MethodParameter showUserCustomMetaAnnotation() {
+		return getMethodParameter("showUserCustomMetaAnnotation", String.class);
+	}
+
+	public MethodParameter showUserCustomMetaAnnotationTpl() {
+		return getMethodParameter("showUserCustomMetaAnnotationTpl", String.class);
+	}
+
 	public MethodParameter showCurrentSecurityWithErrorOnInvalidType() {
 		return getMethodParameter("showCurrentSecurityWithErrorOnInvalidType", SecurityContext.class);
 	}
@@ -394,6 +421,14 @@ public class CurrentSecurityContextArgumentResolverTests {
 		public void showCurrentAuthentication(@CurrentAuthentication Authentication authentication) {
 		}
 
+		public void showUserCustomMetaAnnotation(
+				@AliasedCurrentSecurityContext(expression = "authentication.principal") String name) {
+		}
+
+		public void showUserCustomMetaAnnotationTpl(
+				@CurrentAuthenticationProperty(property = "principal") String name) {
+		}
+
 		public void showCurrentSecurityWithErrorOnInvalidType(
 				@CurrentSecurityWithErrorOnInvalidType SecurityContext context) {
 		}
@@ -447,4 +482,23 @@ public class CurrentSecurityContextArgumentResolverTests {
 
 	}
 
+	@Target({ ElementType.PARAMETER })
+	@Retention(RetentionPolicy.RUNTIME)
+	@CurrentSecurityContext
+	@interface AliasedCurrentSecurityContext {
+
+		@AliasFor(annotation = CurrentSecurityContext.class)
+		String expression() default "";
+
+	}
+
+	@Target({ ElementType.PARAMETER })
+	@Retention(RetentionPolicy.RUNTIME)
+	@CurrentSecurityContext(expression = "authentication.{property}")
+	@interface CurrentAuthenticationProperty {
+
+		String property() default "";
+
+	}
+
 }

+ 58 - 1
web/src/test/java/org/springframework/security/web/reactive/result/method/annotation/CurrentSecurityContextArgumentResolverTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2024 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.
@@ -31,10 +31,12 @@ import reactor.util.context.Context;
 
 import org.springframework.core.MethodParameter;
 import org.springframework.core.ReactiveAdapterRegistry;
+import org.springframework.core.annotation.AliasFor;
 import org.springframework.expression.BeanResolver;
 import org.springframework.expression.spel.SpelEvaluationException;
 import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.Authentication;
+import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
 import org.springframework.security.core.annotation.CurrentSecurityContext;
 import org.springframework.security.core.context.ReactiveSecurityContextHolder;
 import org.springframework.security.core.context.SecurityContext;
@@ -402,6 +404,42 @@ public class CurrentSecurityContextArgumentResolverTests {
 		ReactiveSecurityContextHolder.clearContext();
 	}
 
+	@Test
+	public void resolveArgumentCustomMetaAnnotation() {
+		MethodParameter parameter = ResolvableMethod.on(getClass())
+			.named("showUserCustomMetaAnnotation")
+			.build()
+			.arg(Mono.class, String.class);
+		Authentication auth = buildAuthenticationWithPrincipal("current_authentication");
+		Context context = ReactiveSecurityContextHolder.withAuthentication(auth);
+		Mono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);
+		String principal = (String) argument.contextWrite(context).cast(Mono.class).block().block();
+		assertThat(principal).isSameAs(auth.getPrincipal());
+		ReactiveSecurityContextHolder.clearContext();
+	}
+
+	@Test
+	public void resolveArgumentCustomMetaAnnotationTpl() {
+		this.resolver.setTemplateDefaults(new AnnotationTemplateExpressionDefaults());
+		MethodParameter parameter = ResolvableMethod.on(getClass())
+			.named("showUserCustomMetaAnnotationTpl")
+			.build()
+			.arg(Mono.class, String.class);
+		Authentication auth = buildAuthenticationWithPrincipal("current_authentication");
+		Context context = ReactiveSecurityContextHolder.withAuthentication(auth);
+		Mono<Object> argument = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange);
+		String principal = (String) argument.contextWrite(context).cast(Mono.class).block().block();
+		assertThat(principal).isSameAs(auth.getPrincipal());
+		ReactiveSecurityContextHolder.clearContext();
+	}
+
+	void showUserCustomMetaAnnotation(
+			@AliasedCurrentSecurityContext(expression = "authentication.principal") Mono<String> user) {
+	}
+
+	void showUserCustomMetaAnnotationTpl(@CurrentAuthenticationProperty(property = "principal") Mono<String> user) {
+	}
+
 	void securityContext(@CurrentSecurityContext Mono<SecurityContext> monoSecurityContext) {
 	}
 
@@ -479,6 +517,25 @@ public class CurrentSecurityContextArgumentResolverTests {
 
 	}
 
+	@Target({ ElementType.PARAMETER })
+	@Retention(RetentionPolicy.RUNTIME)
+	@CurrentSecurityContext
+	@interface AliasedCurrentSecurityContext {
+
+		@AliasFor(annotation = CurrentSecurityContext.class)
+		String expression() default "";
+
+	}
+
+	@Target({ ElementType.PARAMETER })
+	@Retention(RetentionPolicy.RUNTIME)
+	@CurrentSecurityContext(expression = "authentication.{property}")
+	@interface CurrentAuthenticationProperty {
+
+		String property() default "";
+
+	}
+
 	static class CustomSecurityContext implements SecurityContext {
 
 		private Authentication authentication;