spring-security - 使用 MockMVC 集成测试 Spring Boot

标签 spring-security spring-boot spring-test spring-test-mvc mockmvc

我在使用 MockMvc 测试 Spring Boot 应用程序时遇到了一些问题。

我有以下测试类:

@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 安全性,如下所示:
@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
}
}

如果我使用 RestTemplate 访问 http://localhost:8080/accounts/1然后我得到了预期的行为(HTTP 401)。

我已经看到其他示例(例如 Spring Boot setup security for testing )建议我 Autowiring FilterChainProxy并使用 WebApplicationContext.addFilters(filterChainProxy) 手动添加过滤器方法。但是,这对我来说实际上失败了( org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.security.web.FilterChainProxy] found )。

我有两个问题:
  • 为什么注入(inject)的 WebApplicationContext 不会自动使用 SpringSecurity 过滤器?即使我可以获取 FilterChainProxy 并手动添加,JavaDoc for EmbeddedWebApplicationContext 状态

  • any {@link Servlet} or {@link Filter} beans defined in the context will be automatically registered with the embedded Servlet container



    结果,我不希望必须手动添加安全过滤器链,因为我(错误地?)由于 Spring Boot 中的自动配置魔法而希望它“正常工作”?
  • 为什么应用上下文中没有 FilterChainProxy?同样,也许我对 AutoConfiguration 的期望是不正确的——但我认为这将被配置为上下文配置的一部分。

  • 提前感谢您的任何建议。

    编辑
  • FilterChainProxy 没有被注入(inject)的原因是因为我的配置设置为

    公共(public)无效配置(WebSecurity web){
    web.debug(true);
    }

  • 这实际上配置了一个 org.springframework.security.web.debug.DebugFilter反而。无论此调试设置如何,我现在设法获取过滤器的方式如下:
    @Resource(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    private Filter securityFilter;
    

    如果我将其添加到 MockMvcBuilder 中,如下所示:
    MockMvcBuilders.webAppContextSetup(webApplicationContext).addFilters(securityFilter)
    

    然后它会按预期工作。

    但是,我不明白为什么 MockMVC 会忽略过滤器,因为这对于测试请求似乎很重要,因为过滤器中可能发生任何可能影响测试结果的事情。此外,这意味着要正确测试,我需要在 servlet 上下文中查找所有过滤器并建立它们的优先级/url 映射并适本地添加它们。这似乎容易出错且不必要。

    最佳答案

    我同意 Mock MVC 可能更多用于测试 Spring MVC 和 Controller 中的自定义代码,由@dave-syer 评论。因此,如果想同时使用您的自定义 Controller 代码(映射到 URL 的 Controller 的正确性;输入和输出对象的映射和验证;标准 Controller ;您的 Controller )来测试 Spring MVC 基础架构,而不利用 Servlet 容器部分堆栈,MockMVC 为您服务。

    但是 MockMVC 也确实有添加过滤器的方法,所以它 设计有可能使过滤器参与所描述的测试类型。有时过滤器可能对 Controller 内部的代码起到功能作用,否则无法使用 MockMVC 进行测试。

    考虑到所有这些理论,我试图为我的测试模拟引导行为,其中过滤器将以 Spring Boot 方式设置并由我的测试拾取以与 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();
    }
    

    关于spring-security - 使用 MockMVC 集成测试 Spring Boot,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26762209/

    相关文章:

    java - Remember Me with Spring Security 3.1.3 - 弃用的默认构造函数

    spring-boot - Spring 数据 R2DBC : org. springframework.data.mapping.PropertyReferenceException:找不到类型的属性 findAll

    java - 跳过 Maven 部署一个模块

    java - 不确定为什么这个单元测试有效。 Spring ConfigurationContext 更新或创建新对象?

    java - 无法从 Spring-Jersey 中检索安全上下文

    java - 我们什么时候在 spring security 中使用 denyAll

    java - SpringBoot : How to make profile specific resources

    Spring Boot 嵌入式 Tomcat 未在 ApplicationListener 中加载外部属性文件

    spring-boot - 我可以使用不同的配置多次运行 `@SpringBootTest` 类吗?

    java - 使用 springframework 的不良 JUnit 测试具有脆弱的 Thread.sleep() 调用。怎么修?