I have implemented websocket in spring but the JavaScript client cannot connect to the websocket.
Here is the WebSocketConfig class:
package com.myapp.spring.security.config;
import com.myapp.spring.web.controller.MyWebSocketHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
//import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.handler.PerConnectionWebSocketHandler;
#Configuration
#EnableWebMvc
#EnableWebSocket
#ComponentScan(basePackages={"com.myapp.spring.*"})
public class WebSocketConfig implements WebSocketConfigurer {
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry
.addHandler(myWebSocketHandler(), "/endpoint")
.setAllowedOrigins("*");
}
#Bean
public WebSocketHandler myWebSocketHandler() {
return new PerConnectionWebSocketHandler(MyWebSocketHandler.class);
}
}
Here is the test.html page that tries to connect to the websocket:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<body>
<p>Going to connect to the WebSocket at /endpoint. Check Console</p>
<button onclick="ss()">Send</button>
</body>
<script type="text/javascript">
var webSocket = new WebSocket("wss://"+document.location.hostname+":8443"+"/endpoint");
webSocket.onopen = function(message) {
processOpen(message);
};
webSocket.onmessage = function(message) {
processMessage(message);
};
webSocket.onclose = function(message) {
processClose(message);
};
webSocket.onerror = function(message) {
processError(message);
};
function processOpen(message) {
console.log("JS: Server Connected... "+message);
}
function processMessage(message) {
console.log("Getting a mess: "+message);
}
function processClose(message) {
console.log("JS: Client disconnected... "+message);
}
function processError(message) { //
console.log("Error occured: "+message);
}
function ss() {
webSocket.send("test");
}
</script>
</html>
I initialized the websocket path to be at /endpoint. This is evident by my server logs which say that this has occurred:
[org.springframework.web.socket.server.support.WebSocketHandlerMapping] (ServerService Thread Pool -- 75) Mapped URL path [/endpoint] onto handler of type [class org.springframework.web.socket.server.support.WebSocketHttpRequestHandler]
When I open up test.html, the connection opens and immediately disconnects. The processOpen(message) and processClose(message) function are immediately called, one after the other. So what am I doing wrong, and how can I fix this?
Your java-script code in test.html looks fine. There could be some issue with Spring Web socket configuration which is closing the connection. Following is the working code for web socket with spring boot. Please compare with your configuration.
Web socket dependency in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
WebSocketHandler class
#Component
public class EchoHandler extends TextWebSocketHandler {
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
TextMessage echoMessage = new TextMessage("Echo :" + message.getPayload());
System.out.println("Sending "+echoMessage.getPayload());
session.sendMessage(echoMessage);
}
}
WebSocket Controller class
#EnableWebSocket
#Controller
public class WSController implements WebSocketConfigurer {
#Autowired
private EchoHandler echoHandler;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(echoHandler, "/echo").setAllowedOrigins("*");
}
}
Spring Boot application class
#SpringBootApplication
public class WSApplication {
public static void main(String[] args) {
SpringApplication.run(WSApplication.class, args);
}
}
Related
I'm using react-stomp as follows:
import React from 'react';
import SockJsClient from 'react-stomp';
render() {
return (
<div>
<SockJsClient url={MY_WS_ENDPOINT + this.wsCredentials}
topics={['/topics/all']}
onMessage={(msg) => { console.log(msg); }}
ref={ (client) => { this.clientRef = client }} />
</div>
);
}
}
The server is built using Springboot, and the configuration is:
security configuration:
#Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpDestMatchers(MY_WS_ENDPOINT + "/**").authenticated()
.anyMessage().authenticated();
}
#Override
protected boolean sameOriginDisabled() {
return true;
}
}
WebSocket configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint(MY_WS_ENDPOINT)
.setAllowedOrigins("*")
.withSockJS();
}
#Override
public void configureMessageBroker(final MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/")
.enableSimpleBroker(WS_SIMPLE_BROKER.toArray(new String[0]));
}
}
After a while, the token expires, and the client continue trying to update status from server getting 401 errors for ever. Is there any approach where I can redirect to login page once the first 401 error is received?
Thanks (sorry, quite noob with this technology).
I'm using spring boot in my backend and vuejs in my front-end . I've defined everything in the guide yet I still don't get my project to work.
FrontEnd
connectToWS() {
this.socket = new Sock("http://localhost:8754/stomp-endpoint");
this.stompClient = Stomp.over(this.socket);
this.stompClient.connect(
{},
frame => {
this.connected = true;
console.log(frame);
this.stompClient.subscribe("/topic/greetings", tick => {
console.log(tick);
this.received_messages.push(JSON.parse(tick.body).content);
});
},
error => {
console.log(error);
this.connected = false;
}
);
},
Backend
WebSocketConfiguration Class :
#Configuration
#EnableWebSocketMessageBroker
public class WebsocketConfiguration implements
WebSocketMessageBrokerConfigurer
{
#Override
public void registerStompEndpoints(StompEndpointRegistry registry)
{
// with sockjs
registry.addEndpoint("/stomp-endpoint")
.setAllowedOrigins("http://localhost:8081")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/app")
.enableSimpleBroker("/topic");
}
}
WebSecurityConfigClass :
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/api/v*/registration/**").permitAll()
.antMatchers("/topic**").permitAll()
.antMatchers("/stomp-endpoint").permitAll()
UPDATE :
The error is actually coming out from the browser . It actually changes the route of the api because it has sockjs integrated into it. I couldn't figure out why the API always gets a /info?t=RandomNumber after it. I need the route to remain the same as I typed it in the front-end.
How can I stop the browser from updating my path. Thanks for helping !
I'm working on a little test project with a simple html page (using javascript) as Frontend and a Spring Boot application for my apis as Backend. I use websocket with stomp and sockJS to keep alive my connection between front and back. My problem is the following : when I test my connection with Postman i have no problems but when I call the api from my javascript I have the following error :
I tried every solutions I've found on internet and now i'm just stuck
Here is my Spring boot App_controller :
#RestController
#CrossOrigin(origins = "*")
public class App_Controller {
int id = 0;
ArrayList<Player> players = new ArrayList<Player>();
public void addTab(Player p){
players.add(p);
}
#MessageMapping("/batch-socket")
#SendTo("/topic/messages")
public String send(String message) throws Exception {
String time = new SimpleDateFormat("HH:mm").format(new Date());
return (message + " : " + time);
}
#RequestMapping (value = "/error", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public String error(){
return "erreur";
}
}
Here is my corsConfiguration :
#Configuration
public class CorsConfig {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry){
registry.addMapping("/**")
.allowCredentials(true)
.allowedOrigins("*")
.allowedHeaders("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.maxAge(3600);
}
};
}
}
Here is my WebSocketConfiguration :
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/topic")
.enableSimpleBroker("/app");
}
#Override public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/batch-socket");
registry.addEndpoint("/batch-socket")
.setAllowedOrigins("*")
.withSockJS();
}
}
And finally here is my function to connect to this WebSocket :
function connect(){
var socket = new SockJS("http://localhost:8080/batch-socket");
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame){
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/messages', function(msgOutput){
console.log(msgOutput);
})
})
}
I tried to just add the #CrossOrigin(origins="*") and #CrossOrigins(origins="http://localhost:63342") and I test multiple possibilities with my CorsConfig.
Thank you for your help in advance
(sorry for my english)
You didn't say how your HTML page with JS is loaded. Is it the same localhost:8080 or maybe local file?
For testing you can tell Chrome to ignore cors restrictions:
chrome --disable-web-security --user-data-dir={some dir}
In your code you are doing registry.addEndpoint() twice, remove one and replace "*" with "http://localhost:63342". Remove all other CORS related code.
I think that you don’t need to create the #bean WebMvcConfigurer corsConfigurer, if it is for a simple test. Could you please do a test removing the registration of that bean and change the order of the annotations?
#CrossOrigin(origins = "http://localhost:63342")
#RestController
I try to build a chat application with HTML,JS,CSS with the libraries STOMPJS and SOCKJS as the frontend and Spring Boot as backend.
My connect code in the frontend looks like this:
function connect() {
var socket = new SockJS("http://localhost:8080/chat");
stompClient = Stomp.over(socket);
stompClient.connect(
{},
function (frame) {
console.log("2");
setConnected(true);
console.log("Connected: " + frame);
stompClient.subscribe("/topic/messages", function (messageOutput) {
showMessageOutput(JSON.parse(messageOutput.body));
});
},
console.log("error")
);
}
As far as I know is the connect function of STOMPJS like this: headers, connectCallback, errorCallback.
It never reach the console.log "2" and it gives me the console.log "error" back.
I activated CORS like this in my backend with setAllowedOriginPatterns("*") in my Spring Boot application:
#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("/chat");
registry.addEndpoint("/chat").setAllowedOriginPatterns("*").withSockJS();
}
}
And the controller looks like this:
#Controller
public class MController {
#MessageMapping("/chat")
#SendTo("/topic/messages")
public OutputMessage send(Message message) throws Exception {
String time = new SimpleDateFormat("HH:mm").format(new Date());
return new OutputMessage(message.getFrom(), message.getText(), time);
}
}
In the screenshot you can see the console.log when I execute the function "connect":
I checked all the documentation of SockJS and StompJS but I couldn't find a solution.
Thank you very much for your answer in advance. :)
I am trying to build an application where the server will keep pushing message to the client in some interval.
I have a simple html file like this.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
<script src="sockjs/sockjs.js"></script>
<script src="stomp/stomp.js"></script>
<div ng-app="myApp" ng-controller="myCtrl">
<button ng-click='connect()'>hi</button>
</div>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.connect = function() {
var socket = new SockJS('http://localhost:8099/myws');
$scope.stompClient = Stomp.over(socket);
$scope.stompClient.connect({}, function (frame) {
console.log('Connected:bhabani ' + frame);
$scope.stompClient.subscribe('http://localhost:8099/topic/jobconfig', function (wsdata) {
console.log("helloooooooooooooooooooooooooooooooooooooooooooooooooo");
console.log(wsdata);
});
});
}
});
</script>
I opened the html file in the file system.
file:///export/data1/test-ws.html in the browser.
Now i have a spring web socket like this.
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Autowired
private GreetingController gc;
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/myws").setAllowedOrigins("*").withSockJS();
new Thread(gc).start();
}
}
Have a greeting controller like this, which should push a message to the topic in some internal
#Component
public class GreetingController implements Runnable{
#Autowired
private SimpMessagingTemplate template;
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
while(true){
try {
System.out.println("Sending");
Thread.sleep(1000);
template.convertAndSend("/topic/jobconfig", new Greeting("hi"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Where i press the connect button i can see connection is established.
But after that i do not see any message coming in the browser which should be pushed from the server.
I am expecting the 'helloooooooooooo' message should be printed in my browser console in each interval.
Change URL in stomp client subscribe code from this http://localhost:8099/topic/jobconfig to this /topic/jobconfig.
$scope.stompClient.subscribe('/topic/jobconfig', function(wsdata) {
console.log("hello");
console.log(wsdata);
});