为了对控制器方法上的PreAuthorize注解的hasRole部分进行单元测试,我需要什么?
我的测试应该成功,因为登录的用户只有两个角色中的一个,但是它失败了,并出现以下Assert错误:
java.lang.AssertionError:状态
预期值:401
实际值:200
下面的代码是一个例子:
@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;
}
}
5条答案
按热度按时间r9f1avp51#
更新
Spring Security 4为与MockMvc集成提供了全面的支持。例如:
问题
问题是设置SecurityContextHolder在此示例中不起作用。原因是SecurityContextPersistenceFilter将使用SecurityContextRepository来尝试并从HttpServletRequest中找出SecurityContext(默认情况下,它使用HttpSession)。它找到(或没有找到)的SecurityContext将覆盖您在SecurityContextHolder上设置的SecurityContext。
解决方案
要确保请求经过身份验证,您需要使用所利用的SecurityContextRepository来关联SecurityContext。默认值为HttpSessionSecurityContextRepository。下面是一个允许您模拟用户登录的示例方法:
如何使用它的细节可能仍然有点模糊,因为您可能不知道如何在MockMvc中访问HttpServletRequest,但请继续阅读,因为有更好的解决方案。
"让一切变得更简单"
如果你想让这个和其他与MockMvc的安全相关的交互更容易,你可以参考gs-spring-security-3.2示例应用程序。在这个项目中,你会发现一些名为SecurityRequestPostProcessors的实用程序,用于处理Spring Security和MockMvc。要使用它们,你可以将前面提到的类复制到你的项目中。使用这个实用程序将允许你编写类似如下的代码:
注意:不需要在请求上设置主体,因为只要用户经过身份验证,Spring Security就会为您建立主体。
您可以在SecurityTests中找到更多的例子。这个项目还将有助于MockMvc和Spring Security之间的其他集成(例如,在执行POST时使用CSRF令牌设置请求)。
默认情况下不包括?
您可能会问为什么默认情况下不包含这个。答案是我们根本没有时间考虑3.2的时间线。示例中的所有代码都可以正常工作,但是我们对命名约定和集成的方式没有足够的信心来发布这个。您可以跟踪SEC-2015,它计划与Spring Security 4.0.0.M1一起发布。
更新
您的MockMvc示例还需要包含springSecurityFilterChain。为此,您可以使用以下代码:
要使
@Autowired
正常工作,您需要确保在@ContextConfiguration
中包含使springSecurityFilterChain生效的安全配置。对于当前设置,这意味着“classpath:/spring/abstract-security-test.xml”应包含安全配置的<http ..>
部分(以及所有依赖bean)。或者,您可以在@ContextConfiguration
中包含第二个文件,该文件包含安全配置的<http ..>
部分(以及所有依赖bean)。nfeuvbwi2#
在上述Rob的解决方案中,截至2014年12月20日,在上述Rob的答案中,master分支上的
SecurityRequestPostProcessors
类中存在一个错误,该错误阻止填充分配的角色。一种快速修复方法是注解掉
SecurityRequestPostProcessors
的UserRequestPostProcessor
内部静态类的roles(String... roles)
方法中的以下代码行(当前为第181行):// List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(roles.length);
.您需要注解掉局部变量,而不是成员变量。
或者,您可以在从方法传回之前插入这一行:
this.authorities = authorities;
如果我有足够的声誉,我会把这句话作为评论加进去。
niwlg2el3#
MockMvcBuilders.standaloneSetup
手动示例化了一个MyController
(没有Spring,因此没有AOP)。因此,PreAuthorize不会被拦截,安全检查也会被跳过。因此,您可以@Autowire您的控制器,并将其传递给MockMvcBuilders.standaloneSetup
,以模拟传递给控制器的任何服务(有时候需要)。使用@MockBean
,这样服务的每个示例都会被替换为Mock。lymnna714#
我遇到了同样的问题,我花了一周的时间来解决这个问题,所以我想在这里分享我的知识,也许它会对以后的人有所帮助。公认的答案或多或少是正确的,但主要的一点是,你必须在你的
abstract-security-test.xml
中声明所有注入的bean,这可能是一个很大的痛苦,当你有很多注入,而且你不想复制一切。所以我用了一个autoBeanMocker
来模拟所有的bean。这个类是这样的:不要忘记将其添加到上下文配置xml文件中。
因此,现在您需要在测试中自动连接控制器:
因为我想模拟一些bean,所以我也在我的控制器上使用了
@InjectMocks
,我在我的setup()
方法中使用了MockitoAnnotations.initMocks(this);
。现在,你应该知道的最后一点是,如果你要将一些bean自动配置到你的控制器中,你需要为它们创建setter方法,否则InjectMocks将无法工作。另外,我不需要将SpringSecurityFilterChain添加到我的控制器中,所以我只需要像这样定义我的mockMvc:
这是一个示例测试方法:
unhi4e5o5#
添加
@WithMockUser(authorities = ["YOUR_ROLE"])
对我来说很有用。当使用MockMcv时,这个自动设置在Spring的安全上下文中的角色。