通过MockMvc

时间:2015-12-11 13:29:41

标签: java spring spring-security spring-boot mockmvc

我正在使用Spring Boot 1.3,Spring 4.2和Spring Security 4.0。我正在使用MockMvc运行集成测试,例如:

mockMvc = webAppContextSetup(webApplicationContext).build();
MvcResult result = mockMvc.perform(get("/"))
            .andExpect(status().isOk())
            .etc;

在我的测试中,我正在模拟这样的用户登录:

CurrentUser principal = new CurrentUser(user);
Authentication auth =
                new UsernamePasswordAuthenticationToken(principal, "dummypassword",
                    principal.getAuthorities());

SecurityContextHolder.getContext().setAuthentication(auth);

这适用于我用@PreAuthorize注释的方法,例如从测试中调用这样的方法时:

@PreAuthorize("@permissionsService.canDoThisThing(principal)")
public void mySecuredMethod()

原则,我的CurrentUser对象,在PermissionsService#canDoThisThing中是非空的。

我有一个用@ControllerAdvice注释的类,它将当前登录的用户添加到模型中,以便可以在每个视图中访问它:

@ControllerAdvice
public class CurrentUserControllerAdvice {
    @ModelAttribute("currentUser")
    public CurrentUser getCurrentUser(Authentication authentication) {
        if (authentication == null) {
            return null;
        }

        return (CurrentUser) authentication.getPrincipal();
    }
}

这在运行应用程序时工作正常(但这是我的问题) - 运行我的测试时,传入上面的getCurrentUser方法的身份验证参数始终为null。这意味着我的视图模板中对currentUser属性的任何引用都会导致错误,因此这些测试失败。

我知道我可以通过检索这样的原理来解决这个问题:

authentication = SecurityContextHolder.getContext().getAuthentication();

但我宁愿不改变我的主要代码,以便测试工作。

1 个答案:

答案 0 :(得分:2)

将Spring Security与SecurityContextHolder一起使用时,设置MockMvc不起作用。原因是Spring Security的SecurityContextPersistenceFilter尝试从SecurityContext解析HttpServletRequest。默认情况下,这是通过在名为HttpSessionSecurityContextRepository的{​​{1}}属性处进行检索,使用HttpSession完成的。属性名称由常量SPRING_SECURITY_CONTEXT定义。无论HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY中找到SecurityContext的是什么,都会在HttpSession上设置,这会覆盖您之前设置的值。

手动解决问题

涉及最少量更改的修补方法是在SecurityContextHolder中设置SecurityContext。您可以使用以下内容执行此操作:

HttpSession

关键是要确保将名为MvcResult result = mockMvc.perform(get("/").sessionAttr(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, securityContext)) .andExpect(status().isOk()) .etc; 的{​​{1}}属性设置为HttpSession。在我们的示例中,我们利用常量SPRING_SECURITY_CONTEXT来定义属性名称。

Spring Security Test

Spring Security 4.0已正式添加test support。到目前为止,这是使用Spring Security测试应用程序的最简单,最灵活的方法。

添加spring-security-test

由于您使用的是Spring Boot,因此确保您具有此依赖关系的最简单方法是在Maven pom中包含spring-security-test。 Spring Boot管理版本,因此无需指定版本。

SecurityContext

当然,Gradle包含非常相似。

设置MockMvc

为了integrate with MockMvc,您必须执行参考中列出的步骤。

第一步是确保您使用HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY。这应该不足为奇,因为这是使用Spring应用程序进行测试时的标准步骤。

next step是为了确保您使用<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> 构建@RunWith(SpringJUnit4ClassRunner.class)实例。例如:

MockMvc

以用户身份运行

现在您可以轻松地与特定用户一起运行。使用MockMvc有两种方法可以实现这一点。

使用RequestPostProcessor

第一个选项是using a RequestPostProcessor。对于您的示例,您可以执行以下操作:

SecurityMockMvcConfigurers.springSecurity()

使用注释

您还可以使用注释来指定用户。由于您使用自定义用户对象(即CurrentUser),因此您可能会考虑使用@WithUserDetails@WithSecurityContext

@WithUserDetails如果将import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @WebAppConfiguration public class MyTests { @Autowired private WebApplicationContext context; private MockMvc mvc; @Before public void setup() { mvc = MockMvcBuilders .webAppContextSetup(context) .apply(springSecurity()) // sets up Spring Security with MockMvc .build(); } ... 公开为bean,并且用户被查找(即它必须存在),那么它是有意义的。 import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*; // use this if CustomUser (principal) implements UserDetails mvc .perform(get("/").with(user(principal))) ... // otherwise use this mvc .perform(get("/").with(authentication(auth))) ... 的示例可能如下所示:

UserDetailsService

另一种方法是使用@WithSecurityContext。如果您不想要求用户实际存在(这是WithUserDetails所必需的),这是有意义的。我不会详细说明这一点,因为它有详细记录,如果没有关于对象模型的更多细节,我无法提供具体的例子。