为了在控制器方法上对 PreAuthorize 注释的 hasRole 部分进行单元测试,我需要什么?
我的测试应该会成功,因为登录用户只有两个角色之一,但它会失败并出现以下断言错误:
java.lang.AssertionError: 状态
预期:401
实际:200
我在 MyController 中有以下方法:
@PreAuthorize(value = "hasRole('MY_ROLE') and hasRole('MY_SECOND_ROLE')")
@RequestMapping(value = "/myurl", method = RequestMethod.GET)
public String loadPage(Model model, Authentication authentication, HttpSession session) {
...stuff to do...
}
我创建了以下 abstract-security-test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">
<security:global-method-security secured-annotations="enabled" />
<security:authentication-manager alias="authManager">
<security:authentication-provider>
<security:user-service>
<security:user name="missingsecondrole" password="user" authorities="MY_ROLE" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans>
在我的单元测试中,我有这个:
@ContextConfiguration("classpath:/spring/abstract-security-test.xml")
public class MyTest {
private final MyController myController = new MyController();
@Autowired
private AuthenticationManager manager;
@Test
public void testValidUserWithInvalidRoleFails() throws Exception {
MockMvc mockMvc = standaloneSetup(myController).setViewResolvers(viewResolver()).build();
Authentication auth = login("missingsecondrole", "user");
mockMvc.perform(get("/myurl")
.session(session)
.flashAttr(MODEL_ATTRIBUTE_NAME, new ModelMap())
.principal(auth)).andExpect(status().isUnauthorized());
}
protected Authentication login(String name, String password) {
Authentication auth = new UsernamePasswordAuthenticationToken(name, password);
SecurityContextHolder.getContext().setAuthentication(manager.authenticate(auth));
return auth;
}
private ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("WEB-INF/views");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
原文由 edwardmlyte 发布,翻译遵循 CC BY-SA 4.0 许可协议
更新
Spring Security 4 为与 MockMvc 的集成提供了 全面的支持。例如:
问题
问题是设置 SecurityContextHolder 在这种情况下不起作用。原因是 SecurityContextPersistenceFilter 将使用 SecurityContextRepository 来尝试从 HttpServletRequest 中找出 SecurityContext(默认情况下它使用 HttpSession)。它找到(或没有找到)的 SecurityContext 将覆盖您在 SecurityContextHolder 上设置的 SecurityContext。
解决方案
为确保请求经过身份验证,您需要使用您正在利用的 SecurityContextRepository 关联您的 SecurityContext。默认是 HttpSessionSecurityContextRepository。下面是一个允许您模拟用户登录的示例方法:
如何使用它的细节可能仍然有点模糊,因为您可能不知道如何访问 MockMvc 中的 HttpServletRequest,但请继续阅读,因为有更好的解决方案。
让它更容易
如果你想让这个和其他安全相关的与 MockMvc 的交互更容易,你可以参考 gs-spring-security-3.2 示例应用程序。在该项目中,您会发现一些用于使用 Spring Security 和 MockMvc 的实用程序,称为 SecurityRequestPostProcessors 。要使用它们,您可以将前面提到的类复制到您的项目中。使用此实用程序将允许您编写如下内容:
注意:不需要在请求中设置委托人,因为只要用户通过身份验证,Spring Security 就会为您建立委托人。
您可以在 SecurityTests 中找到其他示例。该项目还将协助 MockMvc 和 Spring Security 之间的其他集成(即在执行 POST 时使用 CSRF 令牌设置请求)。
默认不包含?
您可能会问为什么默认情况下不包含它。答案是我们根本没有时间处理 3.2 时间线。示例中的所有代码都可以正常工作,但我们对命名约定以及它是如何集成以发布它没有足够的信心。您可以跟踪计划与 Spring Security 4.0.0.M1 一起 发布的 SEC-2015 。
更新
您的 MockMvc 实例还需要包含 springSecurityFilterChain。为此,您可以使用以下内容:
为了使
@Autowired
工作,您需要确保在@ContextConfiguration
中包含使 springSecurityFilterChain 成为可能的安全配置。对于您当前的设置,这意味着“classpath:/spring/abstract-security-test.xml”应该包含您的<http ..>
您的安全配置部分(以及所有相关 bean)。或者,您可以在@ContextConfiguration
中包含第二个文件,其中包含您的<http ..>
您的安全配置部分(以及所有相关 bean)。