在Spring中启用WebSocket的跨域请求

13
我有一个 OpenShift Wildfly 服务器。 我正在使用 Spring MVC 框架构建网站。 我的其中一个网页还使用 WebSocket 连接。 在服务器端,我使用了 @ServerEndpoint 注释和 javax.websocket.* 库来创建我的 WebSocket:
package com.myapp.spring.web.controller;
import java.io.IOException;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.springframework.web.socket.server.standard.SpringConfigurator;


@ServerEndpoint(value="/serverendpoint", configurator = SpringConfigurator.class)

public class serverendpoint {

    @OnOpen
    public void handleOpen () {
        System.out.println("JAVA: Client is now connected...");
    }

    @OnMessage
    public String handleMessage (Session session, String message) throws IOException {

        if (message.equals("ping")) {
//            return "pong"
                session.getBasicRemote().sendText("pong");
        }
        else if (message.equals("close")) {
            handleClose();
            return null;
        }
        System.out.println("JAVA: Received from client: "+ message);
        MyClass mc = new MyClass(message);
        String res = mc.action();
        session.getBasicRemote().sendText(res);
        return res;
    }

    @OnClose
    public void handleClose() {
        System.out.println("JAVA: Client is now disconnected...");
    }

    @OnError
    public void handleError (Throwable t) {
        t.printStackTrace();
    }
}

OpenShift提供了一个默认的URL,因此我的所有网页(HTML文件)都具有共同(规范)的主机名。为了简单起见,我将此URL称为 URL A projectname-domainname.rhclound.com )。我创建了一个别名CNAME,名为 URL B (例如 https://www.mywebsite.tech )。 URL B 是安全的,因为它具有 https
我正在使用JavaScript客户端连接到路径 /serverendpoint 的WebSocket。我在我的html网页文件 test.html 中使用的URI如下:
var wsUri = "wss://" + "projectname-domainname.rhclound.com" + ":8443" + "/serverendpoint";

当我打开URL Aprojectname-domainname.rhclound.com/test)时,WebSocket连接并且一切正常。然而,当我尝试使用URL Bhttps://mywebsite.tech/test)连接WebSocket时,JavaScript客户端会立即连接并断开连接。
这是我收到的控制台消息:

enter image description here

以下是我的JavaScript代码,用于连接WebSocket:

/****** BEGIN WEBSOCKET ******/
            var connectedToWebSocket = false;
            var responseMessage = '';
            var webSocket = null;
            function initWS() {
                connectedToWebSocket = false;
                var wsUri = "wss://" + "projectname-domainname.rhcloud.com" + ":8443" + "/serverendpoint";
                webSocket = new WebSocket(wsUri); // Create a new instance of WebSocket using usUri
                webSocket.onopen = function(message) {
                    processOpen(message);
                };
                webSocket.onmessage = function(message) {
                    responseMessage = message.data;
                    if (responseMessage !== "pong") { // Ping-pong messages to keep a persistent connection between server and client
                        processResponse(responseMessage);
                    }
                    return false;
                };
                webSocket.onclose = function(message) {
                    processClose(message);
                };
                webSocket.onerror = function(message) {
                    processError(message);
                };
                console.log("Exiting initWS()");
            }

            initWS(); //Connect to websocket

            function processOpen(message) {
                connectedToWebSocket = true;
                console.log("JS: Server Connected..."+message);
            }

            function sendMessage(toServer) { // Send message to server
                if (toServer != "close") {
                    webSocket.send(toServer);
                } else {
                    webSocket.close();
                }
            }

            function processClose(message) {
                connectedToWebSocket = false;
                console.log("JS: Client disconnected..."+message);
            }

            function processError(message) { 
                userInfo("An error occurred. Please contact for assistance", true, true);
            }
            setInterval(function() {
                if (connectedToWebSocket) {
                    webSocket.send("ping");
                }
            }, 4000); // Send ping-pong message to server
/****** END WEBSOCKET ******/

经过大量调试和尝试,我得出结论,这个问题是由于Spring框架引起的。这是因为在我的项目中引入Spring Framework之前,URL B可以连接到WebSocket,但是在引入Spring之后就无法连接了。
我在Spring官网上阅读了有关WebSocket Policy的信息。我看到了同源策略,其中指出一个别名URL B无法连接到WebSocket,因为它与URL A不是同一来源。根据文档,为解决此问题,我禁用了WebSockets的同源策略,添加了以下代码。我认为这样做会解决我的错误。以下是我添加的代码:
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer;

@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {

    @Override
    protected boolean sameOriginDisabled() {
        return true;
    }

}

然而,这并没有解决问题,所以我在我的 ApplicationConfig 中添加了以下方法,该类继承自 WebMvcConfigurerAdapter:
@Override
public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**").allowedOrigins("https://www.mywebsite.com");
}

这个也不起作用。然后我尝试了这个:
package com.myapp.spring.security.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class MyCorsFilter {

//  @Bean
//  public FilterRegistrationBean corsFilter() {
//      System.out.println("Filchain");
//      UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
//      CorsConfiguration config = new CorsConfiguration();
//      config.setAllowCredentials(true);
//      config.addAllowedOrigin("https://www.mymt.tech");
//      config.addAllowedHeader("*");
//      config.addAllowedMethod("*");
//      source.registerCorsConfiguration("/**", config);
//      FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
//      bean.setOrder(0);
//      System.out.println("Filchain");
//      return bean;
//  }

     @Bean
     public CorsFilter corsFilter() {
         System.out.println("Filchain");
         UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
         CorsConfiguration config = new CorsConfiguration();
         config.setAllowCredentials(true); // you USUALLY want this
         config.addAllowedOrigin("*");
         config.addAllowedHeader("*");
         config.addAllowedMethod("*");
         config.addAllowedMethod("*");
         source.registerCorsConfiguration("/**", config);
         System.out.println("Filchain");
         return new CorsFilter(source);
     }

}

这个方法也没有成功。
甚至我修改了JS代码中的var wsURI为以下内容: var wsUri = "wss://" + "www.mywebsite.com" + ":8443" + "/serverendpoint"; 然后变成了var wsUri = "wss://" + "mywebsite.com" + ":8443" + "/serverendpoint"; 但是,当我这样做时,谷歌浏览器会显示一个错误,说握手失败了。然而,当我使用这个URL var wsUri = "wss://" + "projectname-domianname.rhcloud.com" + ":8443" + "/serverendpoint";时,我没有收到握手失败的错误,但是我得到一个连接打开并立即关闭的消息(如上所示)。
那么,我该如何解决这个问题呢?

你有没有尝试将WebSocket连接限定在URL B上?只是为了测试它是否能成功建立连接。 - gia
请查看此文档https://spring.io/guides/gs/rest-service-cors/ - Ulphat
如果您的后端和前端存在于同一位置,则可以利用代理:这样就不会发生跨域资源共享。 - Kohányi Róbert
2个回答

2
你尝试实现WebMvcConfigurer并重写方法addCorsMappings()了吗?如果没有,尝试一下这个方法。"最初的回答"
@EnableWebMvc
@Configuration
@ComponentScan
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {

        registry.addMapping("/**")
        .allowedOrigins("*")
        .allowedMethods("GET", "POST")
        .allowedHeaders("Origin", "Accept", "Content-Type", "Authorization")
        .allowCredentials(true)
        .maxAge(3600);

    }

}

1
警告,这可能允许所有来源,请查看此答案以了解如何限制来源。 https://dev59.com/55nga4cB1Zd3GeqPfOeb#67665523 - Sivaram Rasathurai

0

我认为这不是CORS问题,因为在断开连接之前已成功连接。如果是CORS的话,你甚至无法连接。

我认为这是您的DNS和OpenShift之间通信问题,因为WebSocket需要客户端和服务器之间保持打开的持久连接(长期存在)。如果您的DNS(例如CloudFlare或类似的服务)不支持/未配置使用WebSocket,则客户端会立即断开连接,就像您面临的问题一样。


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