Răsfoiți Sursa

Add @WithUserDetails userDetailsServiceBeanName

Fixes gh-3346
Rob Winch 9 ani în urmă
părinte
comite
835ac0a217

+ 13 - 0
docs/manual/src/docs/asciidoc/_includes/test.adoc

@@ -204,6 +204,19 @@ public void getMessageWithUserDetailsCustomUsername() {
 }
 ----
 
+We can also provide an explicit bean name to look up the `UserDetailsService`.
+For example, this test would look up the username of "customUsername" using the `UserDetailsService` with the bean name "myUserDetailsService".
+
+[source,java]
+----
+@T@Test
+@WithUserDetails(value="customUsername", userDetailsServiceBeanName="myUserDetailsService")
+public void getMessageWithUserDetailsServiceBeanName() {
+	String message = messageService.getMessage();
+	...
+}
+----
+
 Like `@WithMockUser` we can also place our annotation at the class level so that every test uses the same user.
 However unlike `@WithMockUser`, `@WithUserDetails` requires the user to exist.
 

+ 10 - 0
test/src/main/java/org/springframework/security/test/context/support/WithUserDetails.java

@@ -60,4 +60,14 @@ public @interface WithUserDetails {
 	 * @return
 	 */
 	String value() default "user";
+
+	/**
+	 * The bean name for the {@link UserDetailsService} to use. If this is not
+	 * provided, then the lookup is done by type and expects only a single
+	 * {@link UserDetailsService} bean to be exposed.
+	 *
+	 * @return the bean name for the {@link UserDetailsService} to use.
+	 * @since 4.1
+	 */
+	String userDetailsServiceBeanName() default "";
 }

+ 9 - 3
test/src/main/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactory.java

@@ -15,6 +15,7 @@
  */
 package org.springframework.security.test.context.support;
 
+import org.springframework.beans.factory.BeanFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
@@ -23,6 +24,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
 
 /**
  * A {@link WithUserDetailsSecurityContextFactory} that works with {@link WithUserDetails}
@@ -37,14 +39,18 @@ import org.springframework.util.Assert;
 final class WithUserDetailsSecurityContextFactory implements
 		WithSecurityContextFactory<WithUserDetails> {
 
-	private UserDetailsService userDetailsService;
+	private BeanFactory beans;
 
 	@Autowired
-	public WithUserDetailsSecurityContextFactory(UserDetailsService userDetailsService) {
-		this.userDetailsService = userDetailsService;
+	public WithUserDetailsSecurityContextFactory(BeanFactory beans) {
+		this.beans = beans;
 	}
 
 	public SecurityContext createSecurityContext(WithUserDetails withUser) {
+		String beanName = withUser.userDetailsServiceBeanName();
+		UserDetailsService userDetailsService = StringUtils.hasLength(beanName)
+				? this.beans.getBean(beanName, UserDetailsService.class)
+				: this.beans.getBean(UserDetailsService.class);
 		String username = withUser.value();
 		Assert.hasLength(username, "value() must be non empty String");
 		UserDetails principal = userDetailsService.loadUserByUsername(username);

+ 10 - 2
test/src/test/java/org/springframework/security/test/context/showcase/WithUserDetailsTests.java

@@ -66,6 +66,14 @@ public class WithUserDetailsTests {
 		assertThat(getPrincipal()).isInstanceOf(CustomUserDetails.class);
 	}
 
+	@Test
+	@WithUserDetails(value="customUsername", userDetailsServiceBeanName="myUserDetailsService")
+	public void getMessageWithUserDetailsServiceBeanName() {
+		String message = messageService.getMessage();
+		assertThat(message).contains("customUsername");
+		assertThat(getPrincipal()).isInstanceOf(CustomUserDetails.class);
+	}
+
 	@EnableGlobalMethodSecurity(prePostEnabled = true)
 	@ComponentScan(basePackageClasses = HelloMessageService.class)
 	static class Config {
@@ -73,12 +81,12 @@ public class WithUserDetailsTests {
 		@Autowired
 		public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
 			auth
-					.userDetailsService(userDetailsService());
+					.userDetailsService(myUserDetailsService());
 		}
 		// @formatter:on
 
 		@Bean
-		public UserDetailsService userDetailsService() {
+		public UserDetailsService myUserDetailsService() {
 			return new CustomUserDetailsService();
 		}
 	}

+ 26 - 2
test/src/test/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactoryTests.java

@@ -16,13 +16,14 @@
 package org.springframework.security.test.context.support;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.beans.factory.BeanFactory;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.userdetails.UserDetails;
@@ -35,6 +36,8 @@ public class WithUserDetailsSecurityContextFactoryTests {
 	private UserDetailsService userDetailsService;
 	@Mock
 	private UserDetails userDetails;
+	@Mock
+	private BeanFactory beans;
 
 	@Mock
 	private WithUserDetails withUserDetails;
@@ -43,7 +46,8 @@ public class WithUserDetailsSecurityContextFactoryTests {
 
 	@Before
 	public void setup() {
-		factory = new WithUserDetailsSecurityContextFactory(userDetailsService);
+		when(beans.getBean(UserDetailsService.class)).thenReturn(userDetailsService);
+		factory = new WithUserDetailsSecurityContextFactory(beans);
 	}
 
 	@Test(expected = IllegalArgumentException.class)
@@ -67,5 +71,25 @@ public class WithUserDetailsSecurityContextFactoryTests {
 		assertThat(context.getAuthentication()).isInstanceOf(
 				UsernamePasswordAuthenticationToken.class);
 		assertThat(context.getAuthentication().getPrincipal()).isEqualTo(userDetails);
+		verify(beans).getBean(UserDetailsService.class);
+		verifyNoMoreInteractions(beans);
+	}
+
+	// gh-3346
+	@Test
+	public void createSecurityContextWithUserDetailsServiceName() {
+		String beanName = "secondUserDetailsServiceBean";
+		String username = "user";
+		when(withUserDetails.value()).thenReturn(username);
+		when(withUserDetails.userDetailsServiceBeanName()).thenReturn(beanName);
+		when(userDetailsService.loadUserByUsername(username)).thenReturn(userDetails);
+		when(beans.getBean(beanName, UserDetailsService.class)).thenReturn(userDetailsService);
+
+		SecurityContext context = factory.createSecurityContext(withUserDetails);
+		assertThat(context.getAuthentication()).isInstanceOf(
+				UsernamePasswordAuthenticationToken.class);
+		assertThat(context.getAuthentication().getPrincipal()).isEqualTo(userDetails);
+		verify(beans).getBean(beanName, UserDetailsService.class);
+		verifyNoMoreInteractions(beans);
 	}
 }

+ 6 - 7
test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserDetailsAuthenticationTests.java

@@ -15,16 +15,20 @@
  */
 package org.springframework.security.test.web.servlet.showcase.secured;
 
+import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
+import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.test.context.support.WithUserDetails;
 import org.springframework.test.context.ContextConfiguration;
@@ -35,11 +39,6 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 import org.springframework.web.context.WebApplicationContext;
 import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 
-import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
-import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
-
 @RunWith(SpringJUnit4ClassRunner.class)
 @ContextConfiguration(classes = WithUserDetailsAuthenticationTests.Config.class)
 @WebAppConfiguration