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 e2c2da31d85..e35bc82ff3f 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 @@ -17,33 +17,26 @@ package org.springframework.security.messaging.context; import java.lang.annotation.Annotation; -import java.lang.reflect.AnnotatedElement; -import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; import org.springframework.core.MethodParameter; -import org.springframework.core.annotation.MergedAnnotation; -import org.springframework.core.annotation.MergedAnnotations; -import org.springframework.core.annotation.RepeatableContainers; -import org.springframework.core.convert.support.DefaultConversionService; 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.lang.NonNull; import org.springframework.messaging.Message; import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver; -import org.springframework.security.authorization.method.AuthenticationPrincipalTemplateDefaults; 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.AuthenticationPrincipal; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.stereotype.Controller; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; -import org.springframework.util.PropertyPlaceholderHelper; import org.springframework.util.StringUtils; /** @@ -106,11 +99,12 @@ public final class AuthenticationPrincipalArgumentResolver implements HandlerMet private ExpressionParser parser = new SpelExpressionParser(); - private AuthenticationPrincipalTemplateDefaults principalTemplateDefaults = new AuthenticationPrincipalTemplateDefaults(); + private AnnotationSynthesizer synthesizer = AnnotationSynthesizers + .requireUnique(AuthenticationPrincipal.class); @Override public boolean supportsParameter(MethodParameter parameter) { - return findMethodAnnotation(AuthenticationPrincipal.class, parameter) != null; + return findMethodAnnotation(parameter) != null; } @Override @@ -120,7 +114,7 @@ public Object resolveArgument(MethodParameter parameter, Message message) { return null; } Object principal = authentication.getPrincipal(); - AuthenticationPrincipal authPrincipal = findMethodAnnotation(AuthenticationPrincipal.class, parameter); + AuthenticationPrincipal authPrincipal = findMethodAnnotation(parameter); String expressionToParse = authPrincipal.expression(); if (StringUtils.hasLength(expressionToParse)) { StandardEvaluationContext context = new StandardEvaluationContext(); @@ -154,69 +148,24 @@ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy secur *

* By default, this value is null, which indicates that templates should * not be resolved. - * @param principalTemplateDefaults - whether to resolve AuthenticationPrincipal - * templates parameters + * @param templateDefaults - whether to resolve AuthenticationPrincipal templates + * parameters * @since 6.4 */ - public void setTemplateDefaults(@NonNull AuthenticationPrincipalTemplateDefaults principalTemplateDefaults) { - Assert.notNull(principalTemplateDefaults, "principalTemplateDefaults cannot be null"); - this.principalTemplateDefaults = principalTemplateDefaults; + public void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) { + this.synthesizer = AnnotationSynthesizers.requireUnique(AuthenticationPrincipal.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. */ @SuppressWarnings("unchecked") - private T findMethodAnnotation(Class annotationClass, MethodParameter parameter) { + private T findMethodAnnotation(MethodParameter parameter) { return (T) this.cachedAttributes.computeIfAbsent(parameter, - (methodParameter) -> findMethodAnnotation(annotationClass, methodParameter, - this.principalTemplateDefaults)); - } - - private static T findMethodAnnotation(Class annotationClass, MethodParameter parameter, - AuthenticationPrincipalTemplateDefaults principalTemplateDefaults) { - T annotation = parameter.getParameterAnnotation(annotationClass); - if (annotation != null) { - return annotation; - } - return MergedAnnotations - .from(parameter.getParameter(), MergedAnnotations.SearchStrategy.TYPE_HIERARCHY, - RepeatableContainers.none()) - .stream(annotationClass) - .map(mapper(annotationClass, principalTemplateDefaults.isIgnoreUnknown(), "expression")) - .findFirst() - .orElse(null); - } - - private static Function, T> mapper(Class annotationClass, - boolean ignoreUnresolvablePlaceholders, String... attrs) { - return (mergedAnnotation) -> { - MergedAnnotation metaSource = mergedAnnotation.getMetaSource(); - if (metaSource == null) { - return mergedAnnotation.synthesize(); - } - PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("{", "}", null, null, - ignoreUnresolvablePlaceholders); - Map stringProperties = new HashMap<>(); - for (Map.Entry property : metaSource.asMap().entrySet()) { - String key = property.getKey(); - Object value = property.getValue(); - String asString = (value instanceof String) ? (String) value - : DefaultConversionService.getSharedInstance().convert(value, String.class); - stringProperties.put(key, asString); - } - Map attrMap = mergedAnnotation.asMap(); - Map properties = new HashMap<>(attrMap); - for (String attr : attrs) { - properties.put(attr, helper.replacePlaceholders((String) attrMap.get(attr), stringProperties::get)); - } - return MergedAnnotation.of((AnnotatedElement) mergedAnnotation.getSource(), annotationClass, properties) - .synthesize(); - }; + (methodParameter) -> this.synthesizer.synthesize(methodParameter.getParameter())); } } 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 206ce69dec6..ce1437f0b06 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 @@ -29,6 +29,7 @@ import org.springframework.core.MethodParameter; import org.springframework.core.annotation.AliasFor; import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContextHolder; @@ -180,6 +181,7 @@ public void resolveArgumentCustomMetaAnnotation() throws Exception { public void resolveArgumentCustomMetaAnnotationTpl() throws Exception { CustomUserPrincipal principal = new CustomUserPrincipal(); setAuthenticationPrincipal(principal); + this.resolver.setTemplateDefaults(new AnnotationTemplateExpressionDefaults()); this.expectedPrincipal = principal.id; assertThat(this.resolver.resolveArgument(showUserCustomMetaAnnotationTpl(), null)).isEqualTo(principal.id); } @@ -302,7 +304,7 @@ public void showUserAnnotation(@AuthenticationPrincipal CustomUserPrincipal user public void showUserCustomAnnotation(@CurrentUser CustomUserPrincipal user) { } - public void showUserCustomMetaAnnotation(@CurrentUser2(expression = "id") int userId) { + public void showUserCustomMetaAnnotation(@CurrentUser2(expression = "principal.id") int userId) { } public void showUserCustomMetaAnnotationTpl(@CurrentUser3(property = "id") int userId) {