Improve @AuthenticationPrincipal meta-annotations

Closes gh-15286
This commit is contained in:
DingHao
2024-07-02 16:44:15 +08:00
committed by Josh Cummings
parent 6d657ea3da
commit 9aaf959400
8 changed files with 340 additions and 68 deletions

View File

@@ -17,15 +17,19 @@
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.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;
@@ -86,6 +90,7 @@ import org.springframework.web.method.support.ModelAndViewContainer;
* </pre>
*
* @author Rob Winch
* @author DingHao
* @since 4.0
*/
public final class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {
@@ -93,13 +98,18 @@ public final class AuthenticationPrincipalArgumentResolver implements HandlerMet
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy();
private final Map<MethodParameter, Annotation> cachedAttributes = new ConcurrentHashMap<>();
private ExpressionParser parser = new SpelExpressionParser();
private AnnotationSynthesizer<AuthenticationPrincipal> synthesizer = AnnotationSynthesizers
.requireUnique(AuthenticationPrincipal.class);
private BeanResolver beanResolver;
@Override
public boolean supportsParameter(MethodParameter parameter) {
return findMethodAnnotation(AuthenticationPrincipal.class, parameter) != null;
return findMethodAnnotation(parameter) != null;
}
@Override
@@ -110,7 +120,7 @@ public final class AuthenticationPrincipalArgumentResolver implements HandlerMet
return null;
}
Object principal = authentication.getPrincipal();
AuthenticationPrincipal annotation = findMethodAnnotation(AuthenticationPrincipal.class, parameter);
AuthenticationPrincipal annotation = findMethodAnnotation(parameter);
String expressionToParse = annotation.expression();
if (StringUtils.hasLength(expressionToParse)) {
StandardEvaluationContext context = new StandardEvaluationContext();
@@ -148,26 +158,29 @@ public final class AuthenticationPrincipalArgumentResolver implements HandlerMet
this.securityContextHolderStrategy = securityContextHolderStrategy;
}
/**
* Configure AuthenticationPrincipal template resolution
* <p>
* By default, this value is <code>null</code>, which indicates that templates should
* not be resolved.
* @param templateDefaults - whether to resolve AuthenticationPrincipal templates
* parameters
* @since 6.4
*/
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.
*/
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()));
}
}

View File

@@ -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.AuthenticationPrincipal;
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 Authentication
*
* @author Rob Winch
* @author DingHao
* @since 5.0
*/
public class AuthenticationPrincipalArgumentResolver extends HandlerMethodArgumentResolverSupport {
private final Map<MethodParameter, Annotation> cachedAttributes = new ConcurrentHashMap<>();
private ExpressionParser parser = new SpelExpressionParser();
private AnnotationSynthesizer<AuthenticationPrincipal> synthesizer = AnnotationSynthesizers
.requireUnique(AuthenticationPrincipal.class);
private BeanResolver beanResolver;
public AuthenticationPrincipalArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {
@@ -66,7 +76,7 @@ public class AuthenticationPrincipalArgumentResolver extends HandlerMethodArgume
@Override
public boolean supportsParameter(MethodParameter parameter) {
return findMethodAnnotation(AuthenticationPrincipal.class, parameter) != null;
return findMethodAnnotation(parameter) != null;
}
@Override
@@ -82,7 +92,7 @@ public class AuthenticationPrincipalArgumentResolver extends HandlerMethodArgume
}
private Object resolvePrincipal(MethodParameter parameter, Object principal) {
AuthenticationPrincipal annotation = findMethodAnnotation(AuthenticationPrincipal.class, parameter);
AuthenticationPrincipal annotation = findMethodAnnotation(parameter);
String expressionToParse = annotation.expression();
if (StringUtils.hasLength(expressionToParse)) {
StandardEvaluationContext context = new StandardEvaluationContext();
@@ -118,26 +128,29 @@ public class AuthenticationPrincipalArgumentResolver extends HandlerMethodArgume
return !ClassUtils.isAssignable(typeToCheck, principal.getClass());
}
/**
* Configure AuthenticationPrincipal template resolution
* <p>
* By default, this value is <code>null</code>, which indicates that templates should
* not be resolved.
* @param templateDefaults - whether to resolve AuthenticationPrincipal templates
* parameters
* @since 6.4
*/
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.
*/
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()));
}
}