使用Spring WebSocket的SockJS客户端 - 跨源资源共享(CORS)

3
我正在尝试使用Spring Sockets实现Websockets,在前端我使用sockjs、stomp和angular 2。当我尝试将sockjs连接到Spring时,我遇到了以下错误:

在浏览器控制台中:

stomp.min.js:8 Opening Web Socket...
sockjs.min.js:2 WebSocket connection to 'ws://localhost:8080//holmes-web/hello/501/c4oj5wjv/websocket' failed: Error during WebSocket handshake: Unexpected response code: 500
zone.js:101 POST http://localhost:8080//holmes-web/hello/501/rpawmcjq/xhr_streaming?t=1466104769153 scheduleTask @ zone.js:101ZoneDelegate.scheduleTask @ zone.js:336Zone.scheduleMacroTask @ zone.js:273(anonymous function) @ zone.js:122send @ VM1433:3r._start @ sockjs.min.js:2(anonymous function) @ sockjs.min.js:2ZoneDelegate.invokeTask @ zone.js:356onInvokeTask @ core.umd.js:6066ZoneDelegate.invokeTask @ zone.js:355Zone.runTask @ zone.js:256ZoneTask.invoke @ zone.js:423
:3000/#/sockjs-test:1 XMLHttpRequest cannot load http://localhost:8080//holmes-web/hello/501/rpawmcjq/xhr_streaming?t=1466104769153. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. The response had HTTP status code 403.
about:blank:1 Refused to display 'http://localhost:8080//holmes-web/hello/iframe.html#kydolgh3' in a frame because it set 'X-Frame-Options' to 'DENY'.
about:blank:1 Refused to display 'http://localhost:8080//holmes-web/hello/iframe.html#42qmp3pf' in a frame because it set 'X-Frame-Options' to 'DENY'.
zone.js:101 POST http://localhost:8080//holmes-web/hello/501/krvk3vgm/xhr?t=1466104770537 scheduleTask @ zone.js:101ZoneDelegate.scheduleTask @ zone.js:336Zone.scheduleMacroTask @ zone.js:273(anonymous function) @ zone.js:122send @ VM1433:3r._start @ sockjs.min.js:2(anonymous function) @ sockjs.min.js:2ZoneDelegate.invokeTask @ zone.js:356onInvokeTask @ core.umd.js:6066ZoneDelegate.invokeTask @ zone.js:355Zone.runTask @ zone.js:256ZoneTask.invoke @ zone.js:423
:3000/#/sockjs-test:1 XMLHttpRequest cannot load http://localhost:8080//holmes-web/hello/501/krvk3vgm/xhr?t=1466104770537. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. The response had HTTP status code 403.
about:blank:1 Refused to display 'http://localhost:8080//holmes-web/hello/iframe.html#0u5cuqn2' in a frame because it set 'X-Frame-Options' to 'DENY'.
sockjs.min.js:3 GET http://localhost:8080//holmes-web/hello/501/z2a5f4hz/jsonp?c=_jp.a2cgyca r._createScript @ sockjs.min.js:3r @ sockjs.min.js:2n._scheduleReceiver @ sockjs.min.js:2n @ sockjs.min.js:2n @ sockjs.min.js:2r @ sockjs.min.js:2r._connect @ sockjs.min.js:2r._transportClose @ sockjs.min.js:2r._transportTimeout @ sockjs.min.js:2ZoneDelegate.invokeTask @ zone.js:356onInvokeTask @ core.umd.js:6066ZoneDelegate.invokeTask @ zone.js:355Zone.runTask @ zone.js:256ZoneTask.invoke @ zone.js:423
stomp.min.js:8 Whoops! Lost connection to http://localhost:8080//holmes-web/hello

在后台控制台中:
Jun 17, 2016 1:08:48 AM org.apache.catalina.core.StandardWrapperValve 

invoke
SEVERE: Servlet.service() for servlet [holmes] in context with path [/holmes-web] threw exception [Request processing failed; nested exception is org.springframework.web.socket.sockjs.SockJsException: Uncaught failure in SockJS request, uri=http://localhost:8080//holmes-web/hello/544/feziyojb/jsonp?c=_jp.a24dbmq; nested exception is org.springframework.web.socket.sockjs.SockJsException: Uncaught failure for request http://localhost:8080//holmes-web/hello/544/feziyojb/jsonp?c=_jp.a24dbmq; nested exception is java.lang.IllegalArgumentException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml. Also you must use a Servlet 3.0+ container] with root cause
java.lang.IllegalArgumentException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml. Also you must use a Servlet 3.0+ container
    at org.springframework.util.Assert.isTrue(Assert.java:65)
    at org.springframework.http.server.ServletServerHttpAsyncRequestControl.<init>(ServletServerHttpAsyncRequestControl.java:58)
    at org.springframework.http.server.ServletServerHttpRequest.getAsyncRequestControl(ServletServerHttpRequest.java:213)
    at org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession.handleInitialRequest(AbstractHttpSockJsSession.java:200)
    at org.springframework.web.socket.sockjs.transport.handler.AbstractHttpSendingTransportHandler.handleRequestInternal(AbstractHttpSendingTransportHandler.java:68)
    at org.springframework.web.socket.sockjs.transport.handler.JsonpPollingTransportHandler.handleRequestInternal(JsonpPollingTransportHandler.java:79)
    at org.springframework.web.socket.sockjs.transport.handler.AbstractHttpSendingTransportHandler.handleRequest(AbstractHttpSendingTransportHandler.java:58)
    at org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService.handleTransportRequest(TransportHandlingSockJsService.java:256)
    at org.springframework.web.socket.sockjs.support.AbstractSockJsService.handleRequest(AbstractSockJsService.java:328)
    at org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler.handleRequest(SockJsHttpRequestHandler.java:90)
    at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:51)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:624)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at com.thetransactioncompany.cors.CORSFilter.doFilter(CORSFilter.java:209)
    at com.thetransactioncompany.cors.CORSFilter.doFilter(CORSFilter.java:244)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:154)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:85)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:344)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1079)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

我的配置类是WebSocketConfig
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/hello").withSockJS();
    }

}

MvcWebApplicationInitializer(MVC Web 应用程序初始化器)
public class MvcWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

@Override
  protected void customizeRegistration(ServletRegistration.Dynamic registration) {
    registration.setInitParameter("dispatchOptionsRequest", "true");
    registration.setAsyncSupported(true);
  }

@Override
protected Class<?>[] getRootConfigClasses() {
    return new Class[] { SecurityConfiguration.class, WebSocketConfig.class };
}

@Override
protected Class<?>[] getServletConfigClasses() {
    // TODO Auto-generated method stub
    return new Class<?>[] { WebConfig.class };
}

@Override
protected String[] getServletMappings() {
    // TODO Auto-generated method stub
    return new String[] { "/" };
}

// ...其他覆盖... }

web.xml

<?xml version="1.0" encoding="UTF-8"?>


<web-app
        version="3.0"
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <display-name>Holmes</display-name>

    <!-- <filter>
        <filter-name>CorsFilter</filter-name>
        <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CorsFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping> 

    <filter>
        <filter-name>CORSFilter</filter-name>
        <filter-class>com.beehyv.holmes.filters.CORSFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CORSFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>-->
    <filter>
        <!-- The CORS filter with parameters -->
        <filter-name>CORS</filter-name>
        <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
        <async-supported>true</async-supported>

        <init-param>
            <param-name>cors.allowGenericHttpRequests</param-name>
            <param-value>true</param-value>
        </init-param>

        <init-param>
            <param-name>cors.allowOrigin</param-name>
            <param-value>*</param-value>
        </init-param>

        <init-param>
            <param-name>cors.allowSubdomains</param-name>
            <param-value>false</param-value>
        </init-param>

        <init-param>
            <param-name>cors.supportedMethods</param-name>
            <param-value>GET, HEAD, POST, GET, PUT, OPTIONS</param-value>
        </init-param>

        <init-param>
            <param-name>cors.supportedHeaders</param-name>
            <param-value>*</param-value>
        </init-param>

        <init-param>
            <param-name>cors.exposedHeaders</param-name>
            <param-value>X-Test-1, X-Test-2</param-value>
        </init-param>

        <init-param>
            <param-name>cors.supportsCredentials</param-name>
            <param-value>true</param-value>
        </init-param>

        <init-param>
            <param-name>cors.maxAge</param-name>
            <param-value>3600</param-value>
        </init-param>

    </filter>
    <filter-mapping>
            <filter-name>CORS</filter-name>
            <url-pattern>/*</url-pattern>
    </filter-mapping>

</web-app>

你正在使用哪个容器和版本?您能否禁用所有过滤器和安全性以检查问题是否仍然存在? - Brian Clozel
@BrianClozel,我已经尝试禁用所有过滤器。我仅使用的过滤器是CORsfilter,如果我禁用它,就会收到一个跨域错误。我猜问题不在过滤器配置上,而在于Spring Socket的异步配置。 - Roy
1个回答

0
在您的web.xml文件中,您必须将您的servlet添加为true。不仅适用于过滤器,也适用于servlet。 例如:
<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/appServlet/servlet-context.xml
        </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
</servlet>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接