【问题标题】:Spring boot test fails with No qualifying bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath'Spring 引导测试失败,没有“org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath”类型的限定 bean
【发布时间】:2019-07-25 09:58:24
【问题描述】:

我有一个运行时可以运行的 Spring Boot 应用程序,但是当我尝试使用 MockMVC 进行一些测试时出现错误。错误是由SecurityConfig中的一行触发的:

val publicUrls = arrayOf(
    "/telemetry/**",
    "/login",
    "/logout"
)

override fun configure(http: HttpSecurity) {
    http
        .csrf().ignoringAntMatchers(*publicUrls)
        .and()
        .authorizeRequests()
        .requestMatchers(EndpointRequest.to("status", "info")).permitAll()

        // The following line is the problem
        .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()   // <-- causes error

        .antMatchers(*publicUrls).permitAll()
        .antMatchers("/system/**").hasRole("SUPER")
        .antMatchers("/admin/**").hasRole("ADMIN")
        .anyRequest().authenticated()
    //.and()
    //.oauth2Login();
}

如果没有允许访问静态资源的行,则测试可以正常工作。有了这条线,我得到了下面的错误(带有堆栈跟踪)。如果我添加一个实现DispatcherServletPath 的类,并用@Component 注释,那么我会得到一个错误expected single matching bean but found 2:

测试代码为:

@SpringBootTest
@AutoConfigureMockMvc
@WebAppConfiguration
@ComponentScan("com.controlj.monitor.service")
@RunWith(SpringJUnit4ClassRunner::class)
class SampleTest {

    @MockBean
    private lateinit var mockTelemetryService: TelemetryService
    @Autowired
    private lateinit var springSecurityFilterChain: FilterChainProxy

    private lateinit var mvc: MockMvc

    @Before
    fun setup() {
        mvc = MockMvcBuilders.standaloneSetup(TelemetryEndpoint(mockTelemetryService))
            .apply<StandaloneMockMvcBuilder>(SecurityMockMvcConfigurers.springSecurity(springSecurityFilterChain))
            .build()
    }

   @Test
    fun testTelemetryApi() {
        val postMapping = TelemetryEndpoint::post.findAnnotation<PostMapping>()?.value?.first()
        mvc.perform(post(postMapping, "badKey", 123456)
            .contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().`is`(404))
   }
}

我该如何解决这个问题?

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath' available

at org.springframework.beans.factory.support.StaticListableBeanFactory.getBean(StaticListableBeanFactory.java:172)
at org.springframework.test.web.servlet.setup.StubWebApplicationContext.getBean(StubWebApplicationContext.java:176)
at org.springframework.boot.security.servlet.ApplicationContextRequestMatcher.lambda$createContext$1(ApplicationContextRequestMatcher.java:95)
at org.springframework.boot.autoconfigure.security.servlet.StaticResourceRequest$StaticResourceRequestMatcher.initialized(StaticResourceRequest.java:140)
at org.springframework.boot.security.servlet.ApplicationContextRequestMatcher.getContext(ApplicationContextRequestMatcher.java:73)
at org.springframework.boot.security.servlet.ApplicationContextRequestMatcher.matches(ApplicationContextRequestMatcher.java:57)
at org.springframework.security.web.access.intercept.DefaultFilterInvocationSecurityMetadataSource.getAttributes(DefaultFilterInvocationSecurityMetadataSource.java:95)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:197)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:124)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:182)

【问题讨论】:

  • 请添加您的测试。

标签: spring-boot spring-mvc spring-security


【解决方案1】:

尝试为您自己的 DispatcherServletPath 实现命名

@Component("dispatcherServlet")
public class Your_Dispatch_Servlet implements DispatcherServletPath {
}

【讨论】:

  • 正如我的问题中所述,添加一个组件来满足这一点会导致错误No qualifying bean of type 'org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath' available: expected single matching bean but found 2: 如果我使用@Component("dispatcherServlet") 注释添加您的代码,我会得到一个不同的错误:org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'dispatcherServlet'
【解决方案2】:

多年来,我一直在为此烦恼。对我来说,一些测试通过或失败取决于我如何运行它们,或者我是一起运行还是单独运行它们——这显然并不理想。但似乎我的安全配置中的同一行导致了我的问题。

我确定这不是最终解决方案,但作为一种解决方法,我现在已经更换了

PathRequest.toStaticResources().atCommonLocations()

new AntPathRequestMatcher("/webjars/**", "GET")

我只使用来自WebJars 的静态资源,所以这足以让我暂时克服这个问题。不过,我想问题实际上出在其他地方。我很想知道到底发生了什么。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-29
    • 2015-03-27
    • 2016-11-17
    • 2016-11-30
    • 1970-01-01
    • 2018-05-01
    • 2014-04-29
    • 1970-01-01
    相关资源
    最近更新 更多