Habilitando solicitações de origem cruzada para WebSockets na primavera
Eu tenho um servidor OpenShift Wildfly. Estou construindo um site com oSpring MVC
estrutura. Uma das minhas páginas da web também usa uma conexão WebSocket. No lado do servidor, usei o@ServerEndpoint
anotação ejavax.websocket.*
biblioteca para criar meu 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();
}
}
O OpenShift fornece uma URL padrão, para que todas as minhas páginas da web (arquivos html) tenham o nome de host comum (canônico). Por uma questão de simplicidade, estou chamando este URLURL A
(projectname-domainname.rhclound.com
) Eu criei um alias, CNAME, deURL A
, que é chamadoURL B
(dizerhttps://www.mywebsite.tech
)URL B
é seguro, pois tem ohttps
.
Estou usando um cliente JavaScript para conectar-se ao WebSocket no caminho/serverendpoint
. O URI que estou usando no meu arquivo de página da web html,test.html
, é o seguinte:
var wsUri = "wss://" + "projectname-domainname.rhclound.com" + ":8443" + "/serverendpoint";
Quando eu abroURL A
(projectname-domainname.rhclound.com/test
), o WebSocket se conecta e tudo funciona bem. No entanto, quando tento conectar-me ao soquete da Web usandoURL B
(https://mywebsite.tech/test
), o cliente JavaScript se conecta e desconecta imediatamente.
Aqui está a mensagem do console que eu recebo:
Aqui está o meu código JavaScript que se conecta ao 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 ******/
Depois de muita depuração e tentativa de várias coisas, concluí que esse problema estava ocorrendo devido ao Spring Framework.Isso ocorre porque antes de apresentar oSpring Framework
no meu projeto,URL B
poderia se conectar ao WebSocket, mas após a introdução do Spring, ele não pode.
Eu li sobresite da primavera sobre a política do WebSocket. Me deparei com elesmesma política de origem que afirma que um apelido,URL B
, não pode se conectar ao WebSocket porque não é a mesma origem queURL A
é. Para resolver este problema eudesativou a mesma política de origem com WebSockets como dito na documentação, então adicionei o seguinte código. Eu pensei que isso iria corrigir o meu erro. Aqui está o que eu adicionei:
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;
}
}
No entanto, como isso não resolveu o problema, adicionei o seguinte método ao meuApplicationConfig
qualextends WebMvcConfigurerAdapter
:
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("https://www.mywebsite.com");
}
Isso também não funcionou. Então eu tentei isso:
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);
}
}
Isso também não funcionou.
Eu até mudei ovar wsURI
no código JS para o seguinte:var wsUri = "wss://" + "www.mywebsite.com" + ":8443" + "/serverendpoint";
Entãovar wsUri = "wss://" + "mywebsite.com" + ":8443" + "/serverendpoint";
Quando fiz isso, o Google Chrome me deu um erro, dizendo que o aperto de mão falhou. No entanto, quando eu tenho esse URL,var wsUri = "wss://" + "projectname-domianname.rhcloud.com" + ":8443" + "/serverendpoint";
, Não recebi o erro de que o handshake não ocorreu, mas recebo uma mensagem informando que a conexão foi aberta e fechada imediatamente (como visto acima).
Então, como posso corrigir isso?