#spring-boot #spring-mvc #spring-security
#весенняя загрузка #spring-mvc #spring-безопасность
Вопрос:
У меня есть приложение 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)
Комментарии:
1. Пожалуйста, добавьте свой тест.
Ответ №1:
попробуйте присвоить вашей собственной реализации DispatcherServletPath
имя
@Component("dispatcherServlet")
public class Your_Dispatch_Servlet implements DispatcherServletPath {
}
Комментарии:
1. Как указано в моем вопросе, добавление компонента, удовлетворяющего этому, приводит к ошибке,
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, так что этого достаточно, чтобы я прошел через это на данный момент. Я предполагаю, что проблема на самом деле в другом месте; я хотел бы знать, что происходит на самом деле.