Spring security 用MockMVC集成测试Spring引导
我在用MockMvc测试Spring启动应用程序时遇到了一些问题 我有以下测试课程:Spring security 用MockMVC集成测试Spring引导,spring-security,spring-boot,spring-test,spring-test-mvc,mockmvc,Spring Security,Spring Boot,Spring Test,Spring Test Mvc,Mockmvc,我在用MockMvc测试Spring启动应用程序时遇到了一些问题 我有以下测试课程: @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = {SpringConfiguration.class, SecurityConfiguration.class}) @IntegrationTest({"server.port=8080"}) @WebAppConfiguration public
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {SpringConfiguration.class, SecurityConfiguration.class})
@IntegrationTest({"server.port=8080"})
@WebAppConfiguration
public class DemoTest {
@Autowired
private EmbeddedWebApplicationContext webApplicationContext;
private MockMvc mockMvc;
@Before
public void setUp() throws Exception {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
@Test
public void testGetAccountUnauthenticated() throws Exception {
mockMvc.perform(get("/accounts/1").accept(MediaType.APPLICATION_JSON))
.andExpect(status().isUnauthorized());
}
}
这将导致HTTP 200而不是401。我已启用组件扫描和自动配置,并且在我的SecuityConfiguration类中配置了spring security,如下所示:
@Configuration
@EnableWebSecurity
@EnableWebMvcSecurity // required for use of @AuthenticationPrincipal in MVC controllers.
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
web.debug(true);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//set up authentication.
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
// set up form login
}
}
@Resource(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
private Filter securityFilter;
MockMvcBuilders.webAppContextSetup(webApplicationContext).addFilters(securityFilter)
如果我使用RestTemplate访问http://localhost:8080/accounts/1
然后我得到了预期的行为(HTTP 401)
我看到了其他示例(例如),它们建议我自动连接FilterChainProxy
,并使用WebApplicationContext.addFilters(FilterChainProxy)
方法手动添加过滤器。但是,这对我来说实际上是失败的(org.springframework.beans.factory.NoSuchBeanDefinitionException:找不到[org.springframework.security.web.FilterChainProxy]类型的合格bean
)
我有两个问题:
编辑
- 无法注入FilterChainProxy的原因是我将配置设置为 公共void配置(WebSecurity web){ debug(true); }
org.springframework.security.web.debug.DebugFilter
。现在,无论此调试设置如何,我都能够获得过滤器,其方法如下:
@Configuration
@EnableWebSecurity
@EnableWebMvcSecurity // required for use of @AuthenticationPrincipal in MVC controllers.
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
web.debug(true);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//set up authentication.
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
// set up form login
}
}
@Resource(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
private Filter securityFilter;
MockMvcBuilders.webAppContextSetup(webApplicationContext).addFilters(securityFilter)
如果我将其添加到MockMvcBuilder,如下所示:
@Configuration
@EnableWebSecurity
@EnableWebMvcSecurity // required for use of @AuthenticationPrincipal in MVC controllers.
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
web.debug(true);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//set up authentication.
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
// set up form login
}
}
@Resource(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
private Filter securityFilter;
MockMvcBuilders.webAppContextSetup(webApplicationContext).addFilters(securityFilter)
然后它就如预期的那样工作了
但是,我不明白为什么MockMVC会忽略过滤器,因为这对于测试请求似乎很重要,因为过滤器中可能发生任何可能影响测试结果的事情。此外,这意味着为了正确地进行测试,我需要查找servlet上下文中的所有过滤器,建立它们的优先级/url映射,并适当地添加它们。这似乎很容易出错,而且没有必要。我同意MockMVC可能更适合于测试SpringMVC和控制器中的自定义代码,正如@dave syer所评论的那样。因此,如果您希望同时使用自定义控制器代码(映射到URL的控制器的正确性;输入和输出对象的映射和验证;标准控制器;您的控制器)测试SpringMVC基础设施,而不利用堆栈的Servlet容器部分,MockMVC就可以了 但是MockMVC也有添加过滤器的方法,因此它的设计允许在所描述的测试类型中使用过滤器。有时过滤器可能在控制器内部的代码中扮演功能性角色,否则就无法用MockMVC进行测试 考虑到所有这些理论,我试图为我的测试模拟引导行为,其中过滤器将以Spring引导方式设置,并由我的测试拾取以与MockVMC一起使用。下面是我最后使用的一个片段。它当然可以得到增强,以更精确地模拟引导行为,并提取到一些定制的MockMVCBuilder中
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setUp() {
Collection<Filter> filterCollection = wac.getBeansOfType(Filter.class).values();
Filter[] filters = filterCollection.toArray(new Filter[filterCollection.size()]);
mockMvc = MockMvcBuilders.webAppContextSetup(wac).addFilters(filters).build();
}
@Autowired
私有WebApplicationContext wac;
私有MockMvc-MockMvc;
@以前
公共作废设置(){
Collection filterCollection=wac.getBeansOfType(Filter.class).values();
Filter[]filters=filterCollection.toArray(新过滤器[filterCollection.size());
mockMvc=MockMvcBuilders.webAppContextSetup(wac).addFilters(filters.build();
}
您试过这个吗
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
...
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class AuthorizeTest {
@Autowired
private WebApplicationContext wac;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(wac)
.apply(springSecurity())
.build();
}
...
}
在我的例子中是403,不是401,但是你明白了 好的,所以我还没有解决这个问题。然而,我相信MockMVC只是忽略EmbeddedWebApplicationContext上任何配置的过滤器。在我的调试器中,如果我计算表达式
webApplicationContext.getServletContext().getFilterRegistrations()
,我可以看到一组过滤器,包括“springSecurityFilterChain”。虽然这并没有向我解释为什么不能注入FilterChainProxy,但它也提出了一个问题,即为什么MockMvc会忽略在Servlet上下文中配置的过滤器。正确。这就是为什么它被称为MockMVC(主要用于测试“DispatcherServlet”和SpringMVC)。但是在您的测试中,有一个完整的应用程序在8080端口上运行,所以如果您想进行端到端测试,为什么不使用restmplate
呢?我选择使用MockMVC而不是restmplate的主要原因是,我觉得使用该API可以更好地生成更简洁的测试代码,并符合预期-例如“.andExpect(status().isUnauthorized())”。其次,我觉得作为不同的用户测试发出请求会稍微容易一些,而不必担心传递特殊的cookie值或请求头来确保我作为给定的用户进行了身份验证。不管怎样,我决定完全按照您的建议去做,使用RestTemplate,而不用担心MockMVC。