Add @WithUserDetails userDetailsServiceBeanName

Fixes gh-3346
This commit is contained in:
Rob Winch 2016-03-09 15:35:18 -06:00
parent 618b8a2d83
commit 835ac0a217
6 changed files with 74 additions and 14 deletions

View File

@ -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.

View File

@ -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 "";
}

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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