java - 无法将 Spring 测试与异步 + Spring Security 一起使用

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

我有一个 @RestController 类,它从其端点返回 DeferredResult 对象。在到达这些端点之前,我使用 @EnableWebSecurity 类来设置基本身份验证。我能够使用正确的基本身份验证在本地 curl 这些端点并使其正常工作。但是,通过 spring 测试测试这些端点会导致此异常:

java.lang.ClassCastException: org.springframework.security.web.servletapi.HttpServlet3RequestFactory$SecurityContextAsyncContext cannot be cast to org.springframework.mock.web.MockAsyncCont

我尝试在我的测试类上使用 @WebAppConfiguration@ContextConfiguration(classes = SpringSecurityConfig.class) 注释并设置 MockMvc 我自己(SpringSecurityConfig 只是我实现基本身份验证的类)。我还尝试过仅使用 @SpringBootTest@AutoConfigureMockMvc

这是我当前的测试类:

@RunWith(SpringRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = SpringSecurityConfig.class)
public class PushNotificationControllerSpringTest {

    @MockBean
    BucketDetailsService bucketDetailsService;

    @MockBean
    NotificationService notificationService;

    @Autowired
    protected WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void prepare() {
        mockMvc = MockMvcBuilders.webAppContextSetup(wac).apply(springSecurity()).build();
    }

    @Test
    public void testListBulletins() throws Exception {
        mockMvc.perform(asyncDispatch(
                mockMvc.perform(get("/admin/bulletins").header("Authorization", "Basic YWRtaW46cGFzc3cwcmQ="))
                        .andExpect(status().isOk()).andReturn()));
    }
}

这是我的休息 Controller :

@RestController
@RequestMapping("/admin")
public class PushNotificationController {

    protected static final Logger logger = LoggerFactory.getLogger(PushNotificationController.class);

    @Autowired
    BucketDetailsService bucketDetailsService;

    @GetMapping("/bulletins")
    public DeferredResult<List<BulletinDetails>> listBulletins() {
        DeferredResult<List<BulletinDetails>> deferredResult = new DeferredResult<>();
        logger.debug("Fetching bulletins...");
        bucketDetailsService.listBulletins()
                .whenComplete((res, ex) -> setResult(deferredResult, res, ex, "List bulletins"));
        return deferredResult;
    }

    private Throwable getRootCause(@NotNull Throwable throwable) {
        Throwable ex = ExceptionUtils.getRootCause(throwable);
        return ex == null ? throwable : ex;
    }

    protected <T> void setResult(DeferredResult<T> deferredResult, T res, Throwable ex, String prefix) {
        if (ex != null) {
            ex = getRootCause(ex);
            logger.debug("{} failure - exception: ", prefix, ex);
            deferredResult.setErrorResult(ex);
        } else {
            logger.debug("{} successfully completed - response: {}", prefix, res);
            deferredResult.setResult(res);
        }
    }

}

编辑:在评论后添加完整的堆栈跟踪

java.lang.ClassCastException: org.springframework.security.web.servletapi.HttpServlet3RequestFactory$SecurityContextAsyncContext cannot be cast to org.springframework.mock.web.MockAsyncContext
    at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:72)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:166)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:158)
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:126)
    at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:111)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
    at net.rakugakibox.spring.boot.logback.access.LogbackAccessSecurityAttributesSaveFilter.doFilter(LogbackAccessSecurityAttributesSaveFilter.java:28)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
    at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:84)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
    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.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.www.BasicAuthenticationFilter.doFilterInternal(BasicAuthenticationFilter.java:215)
    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.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
    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.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:357)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:270)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
    at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:165)
    at com.bbmtek.ops.admin.partner.contribution.springtest.PushNotificationControllerSpringTest.testGetClients(PushNotificationControllerSpringTest.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)

最佳答案

这是 Core Spring 中修复的一个错误。

参见SPR-16695了解详情。

如果升级到 Spring Framework 4.3.16、5.0.6 或更高版本,问题就会消失。

如果升级后问题没有消失,请创建新的 JIRA issue描述问题。

关于java - 无法将 Spring 测试与异步 + Spring Security 一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52062901/

相关文章:

java - 通过 GPS 坐标确定国家/地区

Java:.contains 和 .equals

java - 无法使用@ConfigurationProperties 将 map 与spring boot yaml 文件中的复杂键绑定(bind)

java - JPA 在 Spring Boot 中导致 java.lang.NullPointerException

java - 尽管类路径中有 jar 文件,但包 javax.ejb 不存在

java - 使用 java/jsp 的 Internet 连接类型

spring - spring 请求映射和 url 映射有什么区别?

Spring Boot 2、Spring Security 5 和 @WithMockUser

java - 如何从另一个传递表单对象和调用的 Action 方法调用spring Controller @RequestMapping

java - VSCode 中的错误 "The filename, directory name, or volume label syntax is incorrect"