review phase1
This commit is contained in:
parent
678e0b19e0
commit
570eb01733
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2019 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.
|
||||||
|
@ -40,6 +40,7 @@ import java.util.List;
|
||||||
* {@link HandlerMethodArgumentResolver}.
|
* {@link HandlerMethodArgumentResolver}.
|
||||||
*
|
*
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
|
* @author Dan Zheng
|
||||||
* @since 3.2
|
* @since 3.2
|
||||||
*/
|
*/
|
||||||
class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContextAware {
|
class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContextAware {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2019 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.
|
||||||
|
@ -39,6 +39,7 @@ import org.springframework.web.reactive.result.method.annotation.ArgumentResolve
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
|
* @author Dan Zheng
|
||||||
* @since 5.0
|
* @since 5.0
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2019 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.
|
||||||
|
@ -26,7 +26,7 @@ import java.lang.annotation.Target;
|
||||||
* argument.
|
* argument.
|
||||||
*
|
*
|
||||||
* @author Dan Zheng
|
* @author Dan Zheng
|
||||||
* @since 5.2.x
|
* @since 5.2
|
||||||
*
|
*
|
||||||
* See: <a href=
|
* See: <a href=
|
||||||
* "{@docRoot}/org/springframework/security/web/bind/support/CurrentSecurityContextArgumentResolver.html"
|
* "{@docRoot}/org/springframework/security/web/bind/support/CurrentSecurityContextArgumentResolver.html"
|
||||||
|
@ -43,29 +43,21 @@ public @interface CurrentSecurityContext {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
boolean errorOnInvalidType() default false;
|
boolean errorOnInvalidType() default false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If specified will use the provided SpEL expression to resolve the security context. This
|
* If specified will use the provided SpEL expression to resolve the security context. This
|
||||||
* is convenient if users need to transform the result.
|
* is convenient if users need to transform the result.
|
||||||
*
|
*
|
||||||
|
* <pre>
|
||||||
|
* @CurrentSecurityContext(expression = "authentication") Authentication authentication
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* For example, perhaps the user wants to resolve a CustomUser object that is final
|
* if you want to retrieve more object from the authentcation, you can see the following the expression
|
||||||
* and is leveraging a UserDetailsService. This can be handled by returning an object
|
|
||||||
* that looks like:
|
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* <pre>
|
* <pre>
|
||||||
* public class CustomUserUserDetails extends User {
|
* @CurrentSecurityContext(expression = "authentication.principal") Object principal
|
||||||
* // ...
|
|
||||||
* public CustomUser getCustomUser() {
|
|
||||||
* return customUser;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* Then the user can specify an annotation that looks like:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* @CurrentSecurityContext(expression = "authentication")
|
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
* @return the expression to use.
|
* @return the expression to use.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2019 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.
|
||||||
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.security.web.bind.support;
|
package org.springframework.security.web.bind.support;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.expression.BeanResolver;
|
import org.springframework.expression.BeanResolver;
|
||||||
|
@ -32,8 +34,6 @@ import org.springframework.web.context.request.NativeWebRequest;
|
||||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows resolving the {@link SecurityContext} using the
|
* Allows resolving the {@link SecurityContext} using the
|
||||||
* {@link CurrentSecurityContext} annotation. For example, the following
|
* {@link CurrentSecurityContext} annotation. For example, the following
|
||||||
|
@ -69,7 +69,7 @@ import java.lang.annotation.Annotation;
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Dan Zheng
|
* @author Dan Zheng
|
||||||
* @since 5.2.x
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
public final class CurrentSecurityContextArgumentResolver
|
public final class CurrentSecurityContextArgumentResolver
|
||||||
implements HandlerMethodArgumentResolver {
|
implements HandlerMethodArgumentResolver {
|
||||||
|
|
|
@ -40,7 +40,7 @@ import java.lang.annotation.Annotation;
|
||||||
/**
|
/**
|
||||||
* Resolves the SecurityContext
|
* Resolves the SecurityContext
|
||||||
* @author Dan Zheng
|
* @author Dan Zheng
|
||||||
* @since 5.2.x
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
public class CurrentSecurityContextArgumentResolver extends HandlerMethodArgumentResolverSupport {
|
public class CurrentSecurityContextArgumentResolver extends HandlerMethodArgumentResolverSupport {
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package org.springframework.security.web.bind.support;
|
package org.springframework.security.web.bind.support;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.expression.spel.SpelEvaluationException;
|
import org.springframework.expression.spel.SpelEvaluationException;
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
@ -29,15 +32,12 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.core.userdetails.User;
|
import org.springframework.security.core.userdetails.User;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dan Zheng
|
* @author Dan Zheng
|
||||||
* @since 5.2.x
|
* @since 5.2
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class CurrentSecurityContextArgumentResolverTests {
|
public class CurrentSecurityContextArgumentResolverTests {
|
||||||
|
@ -64,6 +64,22 @@ public class CurrentSecurityContextArgumentResolverTests {
|
||||||
assertThat(resolver.supportsParameter(showSecurityContextAnnotation())).isTrue();
|
assertThat(resolver.supportsParameter(showSecurityContextAnnotation())).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveArgumentWithCustomSecurityContext() throws Exception {
|
||||||
|
String principal = "custom_security_context";
|
||||||
|
setAuthenticationPrincipalWithCustomSecurityContext(principal);
|
||||||
|
CustomSecurityContext customSecurityContext = (CustomSecurityContext) resolver.resolveArgument(showAnnotationWithCustomSecurityContext(), null, null, null);
|
||||||
|
assertThat(customSecurityContext.getAuthentication().getPrincipal()).isEqualTo(principal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveArgumentWithCustomSecurityContextTypeMatch() throws Exception {
|
||||||
|
String principal = "custom_security_context_type_match";
|
||||||
|
setAuthenticationPrincipalWithCustomSecurityContext(principal);
|
||||||
|
CustomSecurityContext customSecurityContext = (CustomSecurityContext) resolver.resolveArgument(showAnnotationWithCustomSecurityContext(), null, null, null);
|
||||||
|
assertThat(customSecurityContext.getAuthentication().getPrincipal()).isEqualTo(principal);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveArgumentNullAuthentication() throws Exception {
|
public void resolveArgumentNullAuthentication() throws Exception {
|
||||||
SecurityContext context = SecurityContextHolder.getContext();
|
SecurityContext context = SecurityContextHolder.getContext();
|
||||||
|
@ -142,10 +158,8 @@ public class CurrentSecurityContextArgumentResolverTests {
|
||||||
public void resolveArgumentSecurityContextErrorOnInvalidTypeTrue() throws Exception {
|
public void resolveArgumentSecurityContextErrorOnInvalidTypeTrue() throws Exception {
|
||||||
String principal = "invalid_type_true";
|
String principal = "invalid_type_true";
|
||||||
setAuthenticationPrincipal(principal);
|
setAuthenticationPrincipal(principal);
|
||||||
try {
|
assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> resolver.resolveArgument(showSecurityContextErrorOnInvalidTypeTrue(), null,
|
||||||
resolver.resolveArgument(showSecurityContextErrorOnInvalidTypeTrue(), null, null, null);
|
null, null));
|
||||||
fail("should not reach here");
|
|
||||||
} catch(ClassCastException ex) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MethodParameter showSecurityContextNoAnnotation() {
|
private MethodParameter showSecurityContextNoAnnotation() {
|
||||||
|
@ -156,6 +170,14 @@ public class CurrentSecurityContextArgumentResolverTests {
|
||||||
return getMethodParameter("showSecurityContextAnnotation", SecurityContext.class);
|
return getMethodParameter("showSecurityContextAnnotation", SecurityContext.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MethodParameter showAnnotationWithCustomSecurityContext() {
|
||||||
|
return getMethodParameter("showAnnotationWithCustomSecurityContext", CustomSecurityContext.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodParameter showAnnotationWithCustomSecurityContextTypeMatch() {
|
||||||
|
return getMethodParameter("showAnnotationWithCustomSecurityContextTypeMatch", SecurityContext.class);
|
||||||
|
}
|
||||||
|
|
||||||
private MethodParameter showSecurityContextAuthenticationAnnotation() {
|
private MethodParameter showSecurityContextAuthenticationAnnotation() {
|
||||||
return getMethodParameter("showSecurityContextAuthenticationAnnotation", Authentication.class);
|
return getMethodParameter("showSecurityContextAuthenticationAnnotation", Authentication.class);
|
||||||
}
|
}
|
||||||
|
@ -197,6 +219,12 @@ public class CurrentSecurityContextArgumentResolverTests {
|
||||||
public void showSecurityContextAnnotation(@CurrentSecurityContext SecurityContext context) {
|
public void showSecurityContextAnnotation(@CurrentSecurityContext SecurityContext context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void showAnnotationWithCustomSecurityContext(@CurrentSecurityContext CustomSecurityContext context) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showAnnotationWithCustomSecurityContextTypeMatch(@CurrentSecurityContext(errorOnInvalidType = true) SecurityContext context) {
|
||||||
|
}
|
||||||
|
|
||||||
public void showSecurityContextAuthenticationAnnotation(@CurrentSecurityContext(expression = "authentication") Authentication authentication) {
|
public void showSecurityContextAuthenticationAnnotation(@CurrentSecurityContext(expression = "authentication") Authentication authentication) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,6 +257,26 @@ public class CurrentSecurityContextArgumentResolverTests {
|
||||||
"ROLE_USER"));
|
"ROLE_USER"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setAuthenticationPrincipalWithCustomSecurityContext(Object principal) {
|
||||||
|
CustomSecurityContext csc = new CustomSecurityContext();
|
||||||
|
csc.setAuthentication(new TestingAuthenticationToken(principal, "password",
|
||||||
|
"ROLE_USER"));
|
||||||
|
SecurityContextHolder.setContext(csc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CustomSecurityContext implements SecurityContext {
|
||||||
|
private Authentication authentication;
|
||||||
|
@Override
|
||||||
|
public Authentication getAuthentication() {
|
||||||
|
return authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAuthentication(Authentication authentication) {
|
||||||
|
this.authentication = authentication;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setAuthenticationDetail(Object detail) {
|
private void setAuthenticationDetail(Object detail) {
|
||||||
TestingAuthenticationToken tat = new TestingAuthenticationToken("user", "password",
|
TestingAuthenticationToken tat = new TestingAuthenticationToken("user", "password",
|
||||||
"ROLE_USER");
|
"ROLE_USER");
|
||||||
|
|
|
@ -21,6 +21,9 @@ import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
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.expression.BeanResolver;
|
import org.springframework.expression.BeanResolver;
|
||||||
|
@ -33,15 +36,14 @@ import org.springframework.security.core.context.SecurityContext;
|
||||||
import org.springframework.security.web.method.ResolvableMethod;
|
import org.springframework.security.web.method.ResolvableMethod;
|
||||||
import org.springframework.web.reactive.BindingContext;
|
import org.springframework.web.reactive.BindingContext;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
import reactor.core.publisher.Mono;
|
|
||||||
import reactor.util.context.Context;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dan Zheng
|
* @author Dan Zheng
|
||||||
* @since 5.2.x
|
* @since 5.2
|
||||||
*/
|
*/
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class CurrentSecurityContextArgumentResolverTests {
|
public class CurrentSecurityContextArgumentResolverTests {
|
||||||
|
@ -98,6 +100,17 @@ public class CurrentSecurityContextArgumentResolverTests {
|
||||||
ReactiveSecurityContextHolder.clearContext();
|
ReactiveSecurityContextHolder.clearContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void resolveArgumentWithCustomSecurityContext() throws Exception {
|
||||||
|
MethodParameter parameter = ResolvableMethod.on(getClass()).named("customSecurityContext").build().arg(Mono.class, SecurityContext.class);
|
||||||
|
Authentication auth = buildAuthenticationWithPrincipal("hello");
|
||||||
|
Context context = ReactiveSecurityContextHolder.withSecurityContext(Mono.just(new CustomSecurityContext(auth)));
|
||||||
|
Mono<Object> argument = resolver.resolveArgument(parameter, bindingContext, exchange);
|
||||||
|
CustomSecurityContext securityContext = (CustomSecurityContext) argument.subscriberContext(context).cast(Mono.class).block().block();
|
||||||
|
assertThat(securityContext.getAuthentication()).isSameAs(auth);
|
||||||
|
ReactiveSecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resolveArgumentWithNullAuthentication1() throws Exception {
|
public void resolveArgumentWithNullAuthentication1() throws Exception {
|
||||||
MethodParameter parameter = ResolvableMethod.on(getClass()).named("securityContext").build().arg(Mono.class, SecurityContext.class);
|
MethodParameter parameter = ResolvableMethod.on(getClass()).named("securityContext").build().arg(Mono.class, SecurityContext.class);
|
||||||
|
@ -216,6 +229,8 @@ public class CurrentSecurityContextArgumentResolverTests {
|
||||||
|
|
||||||
void securityContext(@CurrentSecurityContext Mono<SecurityContext> monoSecurityContext) {}
|
void securityContext(@CurrentSecurityContext Mono<SecurityContext> monoSecurityContext) {}
|
||||||
|
|
||||||
|
void customSecurityContext(@CurrentSecurityContext Mono<SecurityContext> monoSecurityContext) {}
|
||||||
|
|
||||||
void securityContextWithAuthentication(@CurrentSecurityContext(expression = "authentication") Mono<Authentication> authentication) {}
|
void securityContextWithAuthentication(@CurrentSecurityContext(expression = "authentication") Mono<Authentication> authentication) {}
|
||||||
|
|
||||||
void securityContextWithDepthPropOptional(@CurrentSecurityContext(expression = "authentication?.principal") Mono<Object> principal) {}
|
void securityContextWithDepthPropOptional(@CurrentSecurityContext(expression = "authentication?.principal") Mono<Object> principal) {}
|
||||||
|
@ -230,7 +245,21 @@ public class CurrentSecurityContextArgumentResolverTests {
|
||||||
|
|
||||||
void errorOnInvalidTypeWhenExplicitTrue(@CurrentSecurityContext(errorOnInvalidType = true) Mono<String> implicit) {}
|
void errorOnInvalidTypeWhenExplicitTrue(@CurrentSecurityContext(errorOnInvalidType = true) Mono<String> implicit) {}
|
||||||
|
|
||||||
|
static class CustomSecurityContext implements SecurityContext {
|
||||||
|
private Authentication authentication;
|
||||||
|
public CustomSecurityContext(Authentication authentication) {
|
||||||
|
this.authentication = authentication;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Authentication getAuthentication() {
|
||||||
|
return authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAuthentication(Authentication authentication) {
|
||||||
|
this.authentication = authentication;
|
||||||
|
}
|
||||||
|
}
|
||||||
private Authentication buildAuthenticationWithPrincipal(Object principal) {
|
private Authentication buildAuthenticationWithPrincipal(Object principal) {
|
||||||
return new TestingAuthenticationToken(principal, "password",
|
return new TestingAuthenticationToken(principal, "password",
|
||||||
"ROLE_USER");
|
"ROLE_USER");
|
||||||
|
|
Loading…
Reference in New Issue