mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-02-22 14:24:48 +00:00
Improve @CurrentSecurityContext meta-annotations
Closes gh-15551
This commit is contained in:
parent
079b5b91f2
commit
ed16c86115
@ -98,6 +98,7 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
|||||||
CurrentSecurityContextArgumentResolver currentSecurityContextArgumentResolver = new CurrentSecurityContextArgumentResolver();
|
CurrentSecurityContextArgumentResolver currentSecurityContextArgumentResolver = new CurrentSecurityContextArgumentResolver();
|
||||||
currentSecurityContextArgumentResolver.setBeanResolver(this.beanResolver);
|
currentSecurityContextArgumentResolver.setBeanResolver(this.beanResolver);
|
||||||
currentSecurityContextArgumentResolver.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
currentSecurityContextArgumentResolver.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||||
|
currentSecurityContextArgumentResolver.setTemplateDefaults(this.templateDefaults);
|
||||||
argumentResolvers.add(currentSecurityContextArgumentResolver);
|
argumentResolvers.add(currentSecurityContextArgumentResolver);
|
||||||
argumentResolvers.add(new CsrfTokenArgumentResolver());
|
argumentResolvers.add(new CsrfTokenArgumentResolver());
|
||||||
}
|
}
|
||||||
|
@ -109,12 +109,14 @@ class ServerHttpSecurityConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
static WebFluxConfigurer authenticationPrincipalArgumentResolverConfigurer(
|
static WebFluxConfigurer authenticationPrincipalArgumentResolverConfigurer(
|
||||||
ObjectProvider<AuthenticationPrincipalArgumentResolver> authenticationPrincipalArgumentResolver) {
|
ObjectProvider<AuthenticationPrincipalArgumentResolver> authenticationPrincipalArgumentResolver,
|
||||||
|
ObjectProvider<CurrentSecurityContextArgumentResolver> currentSecurityContextArgumentResolvers) {
|
||||||
return new WebFluxConfigurer() {
|
return new WebFluxConfigurer() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
|
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
|
||||||
configurer.addCustomResolver(authenticationPrincipalArgumentResolver.getObject());
|
configurer.addCustomResolver(authenticationPrincipalArgumentResolver.getObject(),
|
||||||
|
currentSecurityContextArgumentResolvers.getObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -133,12 +135,14 @@ class ServerHttpSecurityConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
CurrentSecurityContextArgumentResolver reactiveCurrentSecurityContextArgumentResolver() {
|
CurrentSecurityContextArgumentResolver reactiveCurrentSecurityContextArgumentResolver(
|
||||||
|
ObjectProvider<AnnotationTemplateExpressionDefaults> templateDefaults) {
|
||||||
CurrentSecurityContextArgumentResolver resolver = new CurrentSecurityContextArgumentResolver(
|
CurrentSecurityContextArgumentResolver resolver = new CurrentSecurityContextArgumentResolver(
|
||||||
this.adapterRegistry);
|
this.adapterRegistry);
|
||||||
if (this.beanFactory != null) {
|
if (this.beanFactory != null) {
|
||||||
resolver.setBeanResolver(new BeanFactoryResolver(this.beanFactory));
|
resolver.setBeanResolver(new BeanFactoryResolver(this.beanFactory));
|
||||||
}
|
}
|
||||||
|
templateDefaults.ifAvailable(resolver::setTemplateDefaults);
|
||||||
return resolver;
|
return resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ import org.springframework.security.authentication.TestingAuthenticationToken;
|
|||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
|
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
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.authority.AuthorityUtils;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.web.csrf.CsrfToken;
|
import org.springframework.security.web.csrf.CsrfToken;
|
||||||
@ -115,6 +116,15 @@ public class WebMvcSecurityConfigurationTests {
|
|||||||
this.mockMvc.perform(get("/hi")).andExpect(content().string("Hi, Harold!"));
|
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) {
|
private ResultMatcher assertResult(Object expected) {
|
||||||
return model().attribute("result", 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
|
@Controller
|
||||||
static class TestController {
|
static class TestController {
|
||||||
|
|
||||||
@ -158,6 +177,13 @@ public class WebMvcSecurityConfigurationTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/hello")
|
||||||
|
@ResponseBody
|
||||||
|
String getCurrentAuthenticationProperty(
|
||||||
|
@CurrentAuthenticationProperty(property = "principal") String principal) {
|
||||||
|
return principal;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@ -42,6 +42,7 @@ import org.springframework.security.config.web.server.ServerHttpSecurity;
|
|||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
|
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
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.MapReactiveUserDetailsService;
|
||||||
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
import org.springframework.security.core.userdetails.PasswordEncodedUser;
|
||||||
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
|
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
|
||||||
@ -183,6 +184,27 @@ public class ServerHttpSecurityConfigurationTests {
|
|||||||
.isEqualTo("Hi, Harold!");
|
.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
|
@Configuration
|
||||||
static class SubclassConfig extends ServerHttpSecurityConfiguration {
|
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
|
@RestController
|
||||||
static class TestController {
|
static class TestController {
|
||||||
|
|
||||||
@ -296,6 +327,12 @@ public class ServerHttpSecurityConfigurationTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/hello")
|
||||||
|
String getCurrentAuthenticationProperty(
|
||||||
|
@CurrentAuthenticationProperty(property = "principal") String principal) {
|
||||||
|
return principal;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
package org.springframework.security.messaging.handler.invocation.reactive;
|
package org.springframework.security.messaging.handler.invocation.reactive;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
@ -25,7 +27,6 @@ import org.springframework.core.MethodParameter;
|
|||||||
import org.springframework.core.ReactiveAdapter;
|
import org.springframework.core.ReactiveAdapter;
|
||||||
import org.springframework.core.ReactiveAdapterRegistry;
|
import org.springframework.core.ReactiveAdapterRegistry;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
|
||||||
import org.springframework.expression.BeanResolver;
|
import org.springframework.expression.BeanResolver;
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
import org.springframework.expression.ExpressionParser;
|
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.Message;
|
||||||
import org.springframework.messaging.handler.invocation.reactive.HandlerMethodArgumentResolver;
|
import org.springframework.messaging.handler.invocation.reactive.HandlerMethodArgumentResolver;
|
||||||
import org.springframework.security.core.Authentication;
|
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.annotation.CurrentSecurityContext;
|
||||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
@ -88,12 +92,18 @@ import org.springframework.util.StringUtils;
|
|||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
|
* @author DingHao
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgumentResolver {
|
public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgumentResolver {
|
||||||
|
|
||||||
|
private final Map<MethodParameter, Annotation> cachedAttributes = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private ExpressionParser parser = new SpelExpressionParser();
|
private ExpressionParser parser = new SpelExpressionParser();
|
||||||
|
|
||||||
|
private AnnotationSynthesizer<CurrentSecurityContext> synthesizer = AnnotationSynthesizers
|
||||||
|
.requireUnique(CurrentSecurityContext.class);
|
||||||
|
|
||||||
private BeanResolver beanResolver;
|
private BeanResolver beanResolver;
|
||||||
|
|
||||||
private ReactiveAdapterRegistry adapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
|
private ReactiveAdapterRegistry adapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
|
||||||
@ -118,8 +128,7 @@ public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgu
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsParameter(MethodParameter parameter) {
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
return isMonoSecurityContext(parameter)
|
return isMonoSecurityContext(parameter) || findMethodAnnotation(parameter) != null;
|
||||||
|| findMethodAnnotation(CurrentSecurityContext.class, parameter) != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMonoSecurityContext(MethodParameter parameter) {
|
private boolean isMonoSecurityContext(MethodParameter parameter) {
|
||||||
@ -149,7 +158,7 @@ public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgu
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Object resolveSecurityContext(MethodParameter parameter, Object securityContext) {
|
private Object resolveSecurityContext(MethodParameter parameter, Object securityContext) {
|
||||||
CurrentSecurityContext contextAnno = findMethodAnnotation(CurrentSecurityContext.class, parameter);
|
CurrentSecurityContext contextAnno = findMethodAnnotation(parameter);
|
||||||
if (contextAnno != null) {
|
if (contextAnno != null) {
|
||||||
return resolveSecurityContextFromAnnotation(contextAnno, parameter, securityContext);
|
return resolveSecurityContextFromAnnotation(contextAnno, parameter, securityContext);
|
||||||
}
|
}
|
||||||
@ -193,26 +202,28 @@ public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgu
|
|||||||
return !typeToCheck.isAssignableFrom(value.getClass());
|
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}.
|
* 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}
|
* @param parameter the {@link MethodParameter} to search for an {@link Annotation}
|
||||||
* @return the {@link Annotation} that was found or null.
|
* @return the {@link Annotation} that was found or null.
|
||||||
*/
|
*/
|
||||||
private <T extends Annotation> T findMethodAnnotation(Class<T> annotationClass, MethodParameter parameter) {
|
@SuppressWarnings("unchecked")
|
||||||
T annotation = parameter.getParameterAnnotation(annotationClass);
|
private <T extends Annotation> T findMethodAnnotation(MethodParameter parameter) {
|
||||||
if (annotation != null) {
|
return (T) this.cachedAttributes.computeIfAbsent(parameter,
|
||||||
return annotation;
|
(methodParameter) -> this.synthesizer.synthesize(methodParameter.getParameter()));
|
||||||
}
|
|
||||||
Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
|
|
||||||
for (Annotation toSearch : annotationsToSearch) {
|
|
||||||
annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass);
|
|
||||||
if (annotation != null) {
|
|
||||||
return annotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,16 +16,20 @@
|
|||||||
|
|
||||||
package org.springframework.security.messaging.handler.invocation.reactive;
|
package org.springframework.security.messaging.handler.invocation.reactive;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
import org.springframework.core.annotation.SynthesizingMethodParameter;
|
import org.springframework.core.annotation.SynthesizingMethodParameter;
|
||||||
import org.springframework.security.authentication.TestAuthentication;
|
import org.springframework.security.authentication.TestAuthentication;
|
||||||
import org.springframework.security.core.Authentication;
|
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.annotation.CurrentSecurityContext;
|
||||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
@ -171,6 +175,39 @@ public class CurrentSecurityContextArgumentResolverTests {
|
|||||||
assertThat(result.block().getAuthentication().getPrincipal()).isEqualTo(authentication.getPrincipal());
|
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")
|
@SuppressWarnings("unused")
|
||||||
private void monoCustomSecurityContext(Mono<CustomSecurityContext> securityContext) {
|
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 {
|
static class CustomSecurityContext implements SecurityContext {
|
||||||
|
|
||||||
private Authentication authentication;
|
private Authentication authentication;
|
||||||
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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;
|
package org.springframework.security.web.method.annotation;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
|
||||||
import org.springframework.expression.BeanResolver;
|
import org.springframework.expression.BeanResolver;
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
import org.springframework.expression.ExpressionParser;
|
import org.springframework.expression.ExpressionParser;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
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.annotation.CurrentSecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
@ -72,6 +76,7 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
|||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Dan Zheng
|
* @author Dan Zheng
|
||||||
|
* @author DingHao
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
public final class CurrentSecurityContextArgumentResolver implements HandlerMethodArgumentResolver {
|
public final class CurrentSecurityContextArgumentResolver implements HandlerMethodArgumentResolver {
|
||||||
@ -79,14 +84,19 @@ public final class CurrentSecurityContextArgumentResolver implements HandlerMeth
|
|||||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||||
.getContextHolderStrategy();
|
.getContextHolderStrategy();
|
||||||
|
|
||||||
|
private final Map<MethodParameter, Annotation> cachedAttributes = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private ExpressionParser parser = new SpelExpressionParser();
|
private ExpressionParser parser = new SpelExpressionParser();
|
||||||
|
|
||||||
|
private AnnotationSynthesizer<CurrentSecurityContext> synthesizer = AnnotationSynthesizers
|
||||||
|
.requireUnique(CurrentSecurityContext.class);
|
||||||
|
|
||||||
private BeanResolver beanResolver;
|
private BeanResolver beanResolver;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsParameter(MethodParameter parameter) {
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
return SecurityContext.class.isAssignableFrom(parameter.getParameterType())
|
return SecurityContext.class.isAssignableFrom(parameter.getParameterType())
|
||||||
|| findMethodAnnotation(CurrentSecurityContext.class, parameter) != null;
|
|| findMethodAnnotation(parameter) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -96,7 +106,7 @@ public final class CurrentSecurityContextArgumentResolver implements HandlerMeth
|
|||||||
if (securityContext == null) {
|
if (securityContext == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
CurrentSecurityContext annotation = findMethodAnnotation(CurrentSecurityContext.class, parameter);
|
CurrentSecurityContext annotation = findMethodAnnotation(parameter);
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
return resolveSecurityContextFromAnnotation(parameter, annotation, securityContext);
|
return resolveSecurityContextFromAnnotation(parameter, annotation, securityContext);
|
||||||
}
|
}
|
||||||
@ -124,6 +134,19 @@ public final class CurrentSecurityContextArgumentResolver implements HandlerMeth
|
|||||||
this.beanResolver = beanResolver;
|
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,
|
private Object resolveSecurityContextFromAnnotation(MethodParameter parameter, CurrentSecurityContext annotation,
|
||||||
SecurityContext securityContext) {
|
SecurityContext securityContext) {
|
||||||
Object securityContextResult = securityContext;
|
Object securityContextResult = securityContext;
|
||||||
@ -149,24 +172,13 @@ public final class CurrentSecurityContextArgumentResolver implements HandlerMeth
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain the specified {@link Annotation} on the specified {@link MethodParameter}.
|
* 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}
|
* @param parameter the {@link MethodParameter} to search for an {@link Annotation}
|
||||||
* @return the {@link Annotation} that was found or null.
|
* @return the {@link Annotation} that was found or null.
|
||||||
*/
|
*/
|
||||||
private <T extends Annotation> T findMethodAnnotation(Class<T> annotationClass, MethodParameter parameter) {
|
@SuppressWarnings("unchecked")
|
||||||
T annotation = parameter.getParameterAnnotation(annotationClass);
|
private <T extends Annotation> T findMethodAnnotation(MethodParameter parameter) {
|
||||||
if (annotation != null) {
|
return (T) this.cachedAttributes.computeIfAbsent(parameter,
|
||||||
return annotation;
|
(methodParameter) -> this.synthesizer.synthesize(methodParameter.getParameter()));
|
||||||
}
|
|
||||||
Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
|
|
||||||
for (Annotation toSearch : annotationsToSearch) {
|
|
||||||
annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass);
|
|
||||||
if (annotation != null) {
|
|
||||||
return annotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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;
|
package org.springframework.security.web.reactive.result.method.annotation;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.reactivestreams.Publisher;
|
import org.reactivestreams.Publisher;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
@ -25,12 +27,14 @@ import org.springframework.core.MethodParameter;
|
|||||||
import org.springframework.core.ReactiveAdapter;
|
import org.springframework.core.ReactiveAdapter;
|
||||||
import org.springframework.core.ReactiveAdapterRegistry;
|
import org.springframework.core.ReactiveAdapterRegistry;
|
||||||
import org.springframework.core.ResolvableType;
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
|
||||||
import org.springframework.expression.BeanResolver;
|
import org.springframework.expression.BeanResolver;
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
import org.springframework.expression.ExpressionParser;
|
import org.springframework.expression.ExpressionParser;
|
||||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
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.annotation.CurrentSecurityContext;
|
||||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
@ -44,12 +48,18 @@ import org.springframework.web.server.ServerWebExchange;
|
|||||||
* Resolves the {@link SecurityContext}
|
* Resolves the {@link SecurityContext}
|
||||||
*
|
*
|
||||||
* @author Dan Zheng
|
* @author Dan Zheng
|
||||||
|
* @author DingHao
|
||||||
* @since 5.2
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
public class CurrentSecurityContextArgumentResolver extends HandlerMethodArgumentResolverSupport {
|
public class CurrentSecurityContextArgumentResolver extends HandlerMethodArgumentResolverSupport {
|
||||||
|
|
||||||
|
private final Map<MethodParameter, Annotation> cachedAttributes = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private ExpressionParser parser = new SpelExpressionParser();
|
private ExpressionParser parser = new SpelExpressionParser();
|
||||||
|
|
||||||
|
private AnnotationSynthesizer<CurrentSecurityContext> synthesizer = AnnotationSynthesizers
|
||||||
|
.requireUnique(CurrentSecurityContext.class);
|
||||||
|
|
||||||
private BeanResolver beanResolver;
|
private BeanResolver beanResolver;
|
||||||
|
|
||||||
public CurrentSecurityContextArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {
|
public CurrentSecurityContextArgumentResolver(ReactiveAdapterRegistry adapterRegistry) {
|
||||||
@ -65,10 +75,22 @@ public class CurrentSecurityContextArgumentResolver extends HandlerMethodArgumen
|
|||||||
this.beanResolver = beanResolver;
|
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
|
@Override
|
||||||
public boolean supportsParameter(MethodParameter parameter) {
|
public boolean supportsParameter(MethodParameter parameter) {
|
||||||
return isMonoSecurityContext(parameter)
|
return isMonoSecurityContext(parameter) || findMethodAnnotation(parameter) != null;
|
||||||
|| findMethodAnnotation(CurrentSecurityContext.class, parameter) != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMonoSecurityContext(MethodParameter parameter) {
|
private boolean isMonoSecurityContext(MethodParameter parameter) {
|
||||||
@ -108,7 +130,7 @@ public class CurrentSecurityContextArgumentResolver extends HandlerMethodArgumen
|
|||||||
* @return the resolved object from expression.
|
* @return the resolved object from expression.
|
||||||
*/
|
*/
|
||||||
private Object resolveSecurityContext(MethodParameter parameter, SecurityContext securityContext) {
|
private Object resolveSecurityContext(MethodParameter parameter, SecurityContext securityContext) {
|
||||||
CurrentSecurityContext annotation = findMethodAnnotation(CurrentSecurityContext.class, parameter);
|
CurrentSecurityContext annotation = findMethodAnnotation(parameter);
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
return resolveSecurityContextFromAnnotation(annotation, parameter, securityContext);
|
return resolveSecurityContextFromAnnotation(annotation, parameter, securityContext);
|
||||||
}
|
}
|
||||||
@ -162,24 +184,13 @@ public class CurrentSecurityContextArgumentResolver extends HandlerMethodArgumen
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtains the specified {@link Annotation} on the specified {@link MethodParameter}.
|
* 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}
|
* @param parameter the {@link MethodParameter} to search for an {@link Annotation}
|
||||||
* @return the {@link Annotation} that was found or null.
|
* @return the {@link Annotation} that was found or null.
|
||||||
*/
|
*/
|
||||||
private <T extends Annotation> T findMethodAnnotation(Class<T> annotationClass, MethodParameter parameter) {
|
@SuppressWarnings("unchecked")
|
||||||
T annotation = parameter.getParameterAnnotation(annotationClass);
|
private <T extends Annotation> T findMethodAnnotation(MethodParameter parameter) {
|
||||||
if (annotation != null) {
|
return (T) this.cachedAttributes.computeIfAbsent(parameter,
|
||||||
return annotation;
|
(methodParameter) -> this.synthesizer.synthesize(methodParameter.getParameter()));
|
||||||
}
|
|
||||||
Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
|
|
||||||
for (Annotation toSearch : annotationsToSearch) {
|
|
||||||
annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass);
|
|
||||||
if (annotation != null) {
|
|
||||||
return annotation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
import org.springframework.expression.BeanResolver;
|
import org.springframework.expression.BeanResolver;
|
||||||
import org.springframework.expression.spel.SpelEvaluationException;
|
import org.springframework.expression.spel.SpelEvaluationException;
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
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.annotation.CurrentSecurityContext;
|
||||||
import org.springframework.security.core.authority.AuthorityUtils;
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
@ -247,6 +249,23 @@ public class CurrentSecurityContextArgumentResolverTests {
|
|||||||
.resolveArgument(showCurrentSecurityWithErrorOnInvalidTypeMisMatch(), null, null, null));
|
.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() {
|
private MethodParameter showSecurityContextNoAnnotationTypeMismatch() {
|
||||||
return getMethodParameter("showSecurityContextNoAnnotation", String.class);
|
return getMethodParameter("showSecurityContextNoAnnotation", String.class);
|
||||||
}
|
}
|
||||||
@ -307,6 +326,14 @@ public class CurrentSecurityContextArgumentResolverTests {
|
|||||||
return getMethodParameter("showCurrentAuthentication", Authentication.class);
|
return getMethodParameter("showCurrentAuthentication", Authentication.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MethodParameter showUserCustomMetaAnnotation() {
|
||||||
|
return getMethodParameter("showUserCustomMetaAnnotation", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodParameter showUserCustomMetaAnnotationTpl() {
|
||||||
|
return getMethodParameter("showUserCustomMetaAnnotationTpl", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
public MethodParameter showCurrentSecurityWithErrorOnInvalidType() {
|
public MethodParameter showCurrentSecurityWithErrorOnInvalidType() {
|
||||||
return getMethodParameter("showCurrentSecurityWithErrorOnInvalidType", SecurityContext.class);
|
return getMethodParameter("showCurrentSecurityWithErrorOnInvalidType", SecurityContext.class);
|
||||||
}
|
}
|
||||||
@ -394,6 +421,14 @@ public class CurrentSecurityContextArgumentResolverTests {
|
|||||||
public void showCurrentAuthentication(@CurrentAuthentication Authentication authentication) {
|
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(
|
public void showCurrentSecurityWithErrorOnInvalidType(
|
||||||
@CurrentSecurityWithErrorOnInvalidType SecurityContext context) {
|
@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 "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.MethodParameter;
|
||||||
import org.springframework.core.ReactiveAdapterRegistry;
|
import org.springframework.core.ReactiveAdapterRegistry;
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
import org.springframework.expression.BeanResolver;
|
import org.springframework.expression.BeanResolver;
|
||||||
import org.springframework.expression.spel.SpelEvaluationException;
|
import org.springframework.expression.spel.SpelEvaluationException;
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.core.Authentication;
|
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.annotation.CurrentSecurityContext;
|
||||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
@ -402,6 +404,42 @@ public class CurrentSecurityContextArgumentResolverTests {
|
|||||||
ReactiveSecurityContextHolder.clearContext();
|
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) {
|
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 {
|
static class CustomSecurityContext implements SecurityContext {
|
||||||
|
|
||||||
private Authentication authentication;
|
private Authentication authentication;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user