Mar*_*son 5 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;
Run Code Online (Sandbox Code Playgroud)
在我的测试中,我模拟用户登录,如下所示:
CurrentUser principal = new CurrentUser(user);
Authentication auth =
new UsernamePasswordAuthenticationToken(principal, "dummypassword",
principal.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
Run Code Online (Sandbox Code Playgroud)
这对于我用 注释的方法效果很好@PreAuthorize,例如从测试中调用这样的方法时:
@PreAuthorize("@permissionsService.canDoThisThing(principal)")
public void mySecuredMethod()
Run Code Online (Sandbox Code Playgroud)
原理是,我的 CurrentUser 对象在PermissionsService#canDoThisThing.
我有一个用 @ControllerAdvice 注释的类,它将当前登录的用户添加到模型中,以便可以在每个视图中访问它:
@ControllerAdvice
public class CurrentUserControllerAdvice {
@ModelAttribute("currentUser")
public CurrentUser getCurrentUser(Authentication authentication) {
if (authentication == null) {
return null;
}
return (CurrentUser) authentication.getPrincipal();
}
}
Run Code Online (Sandbox Code Playgroud)
这在运行应用程序时工作得很好,但是(这是我的问题) - 当运行我的测试时,传递到上面的 getCurrentUser 方法的身份验证参数始终为空。这意味着我的视图模板中对 currentUser 属性的任何引用都会导致错误,因此这些测试会失败。
我知道我可以通过检索这样的原理来解决这个问题:
authentication = SecurityContextHolder.getContext().getAuthentication();
Run Code Online (Sandbox Code Playgroud)
但我宁愿不改变我的主要代码,以便测试工作。
将 Spring Security 与 一起使用时,设置SecurityContextHolder不起作用MockMvc。原因是 Spring SecuritySecurityContextPersistenceFilter尝试解决SecurityContext来自HttpServletRequest. HttpSessionSecurityContextRepository默认情况下,这是通过检索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;
Run Code Online (Sandbox Code Playgroud)
关键是确保您将HttpSession名为 的属性设置SPRING_SECURITY_CONTEXT为SecurityContext. 在我们的示例中,我们利用常量HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY来定义属性名称。
Spring Security 4.0正式添加了测试支持。这是迄今为止使用 Spring Security 测试应用程序的最简单、最灵活的方法。
由于您使用的是 Spring Boot,因此确保具有此依赖性的最简单方法是在 Maven pom 中包含 spring-security-test 。Spring Boot 管理版本,所以不需要指定版本。
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
Run Code Online (Sandbox Code Playgroud)
当然,Gradle 包含也非常相似。
为了与 MockMvc 集成,您必须执行参考中概述的一些步骤。
第一步是确保您使用@RunWith(SpringJUnit4ClassRunner.class). 这应该不足为奇,因为这是使用 Spring 应用程序进行测试时的标准步骤。
下一步是确保您使用. 例如:MockMvcSecurityMockMvcConfigurers.springSecurity()
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();
}
...
Run Code Online (Sandbox Code Playgroud)
现在您可以轻松地与特定用户一起运行。使用 MockMvc 有两种方法可以实现此目的。
第一个选项是使用 RequestPostProcessor。对于您的示例,您可以执行以下操作:
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)))
...
Run Code Online (Sandbox Code Playgroud)
您还可以使用注释来指定用户。由于您使用自定义用户对象(即 CurrentUser),您可能会考虑使用@WithUserDetails或@WithSecurityContext。
如果您将@WithUserDetails公开UserDetailsService为 bean 并且您可以查找用户(即它必须存在),则 @WithUserDetails 是有意义的。一个示例@WithUserDetails可能如下所示:
@Test
@WithUserDetails("usernameThatIsFoundByUserDetailsService")
public void run() throws Exception {
MvcResult result = mockMvc.perform(get("/"))
.andExpect(status().isOk())
.etc;
}
Run Code Online (Sandbox Code Playgroud)
另一种方法是使用@WithSecurityContext。如果您不想要求用户实际存在(对于 WithUserDetails 来说这是必需的),这是有意义的。我不会对此进行详细说明,因为它有详细记录,并且没有有关对象模型的更多详细信息,我无法提供具体的示例。
| 归档时间: |
|
| 查看次数: |
3577 次 |
| 最近记录: |