The WebSocket protocol is one of the ways applications handle real-time messages. The most common alternatives are long polling and Server-sent Events. Each of these solutions has its advantages and disadvantages. In this article, I’ll show you how to implement WebSocket using Spring Boot. I’ll cover server-side and client-side Settings that communicate with each other using STOMP over the WebSocket protocol.

The server side will be coded entirely in Java. In terms of clients, however, I’ll show snippets written in Java and JavaScript (SockJS), because typically, the WebSocket client is embedded in the front end application. The code example demonstrates how to broadcast messages to multiple users using the Pub-sub model and how to send messages only to a single user. In another part of this article, I’ll briefly discuss WebSocket security and how to ensure that a WebSocket-based solution works even if the environment does not support the WebSocket protocol.

Note that the topic of WebSocket security is covered only briefly here, as it is a very complex subject that could be written in a separate article. For this reason, as well as WebSocket in production in the last section of this article? I suggest that safety Settings should be modified during production until production is ready and safety measures are in place.

1.WebSocket and STOMP protocol

The WebSocket protocol allows two-way communication between applications. It is important to know that HTTP is only used for the initial handshake. After the initial handshake, the HTTP connection is upgraded to the new TCP/IP connection used by the WebSocket.

The WebSocket protocol is a fairly low-level protocol. It defines how to convert a byte stream into a frame. Frames can contain text or binary messages. Because the message itself does not provide any additional information about how to route or process it, it is difficult to implement more complex applications without writing additional code. Fortunately, the WebSocket specification allows the use of subprotocols at a higher application level. STOMP is one of them and is supported by the Spring Framework.

STOMP is a simple text-based messaging protocol that was originally created for scripting languages such as Ruby, Python, and Perl to connect to enterprise-level message brokers. Thanks to STOMP, clients and agents developed in different languages can send and receive messages to each other. The WebSocket protocol is sometimes called Web TCP. By analogy, STOMP is called Web HTTP. It defines frame types that map to WebSocket frames, such as CONNECT, SUBSCRIBE, UNSUBSCRIBE, ACK, or SEND. On the one hand, these commands are very convenient for managing communication, and on the other hand, they allow us to implement solutions with more complex capabilities, such as message validation.

2. Server :Spring Boot and WebSocket

To build the WebSocket server side, we will leverage the Spring Boot framework, which makes it faster to develop stand-alone programs and Web applications in Java. Spring Boot includes the Spring-WebSocket module, which is compatible with the Java WebSocket API standard (JSR-356).

Implementing the WebSocket server side using Spring Boot is not a very complex task, consisting of only a few steps, which we will cover one by one.

* Step 1:* First, add the WebSocket library dependency.

<dependency>
  <groupId>org.springframework.boot</groupId>            
  <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
Copy the code

If you plan to transport messages in JSON format, you may also need to include GSON or Jackson dependencies. You may also need a Security framework, such as Spring Security.

* Step 2:* You can then configure Spring to enable WebSocket and STOMP messaging.

Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

  @Override
  public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/mywebsockets")
        .setAllowedOrigins("mydomain.com").withSockJS();
  }

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

A configureMessageBroker does two things:

  • Create an in-memory message broker that contains one or more targets for sending and receiving messages. In the example above, two target address prefixes are defined:topicandqueue. They follow the following convention: through the PUB-sub model will be totopicThe message prefixed by is delivered to the destination address of all subscribing clients. On the other hand, the target address of a private message is usuallyqueueFor the prefix.
  • Defines the prefixapp, which is used to filter the target addressesControllerIn the@MessageMappingMethod of modification.

Going back to the code snippet-you may have noticed the call to the method withSockJS(), which enables the SockJS backup option. In short, Internet browsers will make our WebSockets work even if they don’t support the WebSocket protocol. I will discuss this topic in further detail.

One more thing needs to be clarified — why we call the setAllowedOrigins() method on the endpoint. This is generally required because the default behavior of WebSocket and SockJS is to accept only same-origin requests. Therefore, if the client and server are in different domains, this method needs to be called to allow communication between them.

* Step 3: * Implement a controller that handles user requests, which will broadcast the received messages to all users subscribed to a particular topic.

This is an example method of sending a message to the target address /topic/news.

@MessageMapping("/news")
@SendTo("/topic/news")
public void broadcastNews(@Payload String message) {
  return message;
}
Copy the code

You can also use SimpMessagingTemplate instead of the @sendto annotation, which you can Autowired within the controller.

@MessageMapping("/news")
public void broadcastNews(@Payload String message) {
  this.simpMessagingTemplate.convertAndSend("/topic/news", message)
}
Copy the code

In later steps, may need to add some other classes to protect endpoints, for example the ResourceServerConfigurerAdapter or WebSecurityConfigurerAdapter Spring Security framework. In addition, it is often beneficial to implement the message model so that the transferred JSON can be mapped to objects.

3.WebSocket client construction

The client implementation is an easier task.

* Step 1: * Assemble the Spring STOMP client

@Autowired
private WebSocketStompClient stompClient;
Copy the code

Step 2: Open the connection

StompSessionHandler sessionHandler = new CustmStompSessionHandler();

StompSession stompSession = stompClient.connect(loggerServerQueueUrl, 
sessionHandler).get();
Copy the code

When this is done, the message can be sent to the destination. The message is sent to all users who subscribe to the topic.

stompSession.send("topic/greetings"."Hello new user");
Copy the code

The following methods can also subscribe to messages

session.subscribe("topic/greetings".this);

@Override
public void handleFrame(StompHeaders headers, Object payload) {
    Message msg = (Message) payload;
    logger.info("Received : " + msg.getText()+ " from : " + 
    msg.getFrom());
}
Copy the code

Sometimes you need to send messages to specific users (for example, when implementing a chat). The client and server sides must then use separate target addresses dedicated to this private session. You can create a name for the destination address by appending a unique identifier to the generic address, such as /queue/chat-user123. HTTP session or STOMP session identifiers can be used for this purpose.

Spring makes it easier to send private messages. We just need to annotate the Controller method with @sendtouser. Then, the target address will be handled by UserDestinationMessageHandler, it depends on the session identifier. On the client side, when the client subscribes to a destination address prefixed with /user, the destination address is translated to a unique destination address for the user. On the server side, the user target address is resolved based on the user’s Principal.

Example code for using the @sendtouser annotation on the server:

@MessageMapping("/greetings")
@SendToUser("/queue/greetings")
public String reply(@Payload String message, Principal user) {
 return  "Hello " + message;
}
Copy the code

Or you can use SimpMessagingTemplate:

String username = ...
this.simpMessagingTemplate.convertAndSendToUser();
   username, "/queue/greetings"."Hello " + username);
Copy the code

Now let’s look at how to implement a JavaScript(SockJS) client that can receive private messages sent by the Java code in the example above. It’s worth noting that WebSockets are part of the HTML5 specification and are supported by most modern browsers (starting with version 10, Internet Explorer supports them).

function connect(a) {
 var socket = new SockJS('/greetings');
 stompClient = Stomp.over(socket);
 stompClient.connect({}, function (frame) {
   stompClient.subscribe('/user/queue/greetings', function (greeting) {
     showGreeting(JSON.parse(greeting.body).name);
   });
 });
}

function sendName(a) {
 stompClient.send("/app/greetings", {}, $("#name").val());
}
Copy the code

You may have noticed that to receive private messages, the client needs to subscribe to the destination address /queue/greetings with the /user prefix. It doesn’t have to worry about any unique identifiers. However, before the client can log in to the application, the server side must initialize the Principal object.

4. The WebSocket secure

Many Web applications use cookie-based authentication; for example, we can use Spring Security to restrict logged-in users from accessing certain pages or controller restrictions. User context security is then maintained through a cookie-based HTTP session that is later associated with a WebSocket or SockJS session created for the user. WebSocket endpoint can be protected like any other requests, for example, in the Spring of WebSecurityConfigurerAdapter implementation.

Web applications today typically use REST apis as a back end, using OAuth/JWT tokens for user authentication and authorization. The WebSocket protocol does not describe how the server authenticates the client during an HTTP handshake. In fact, standard HTTP headers (for example, authorization) are used for this purpose. Unfortunately, not all STOMP clients support it. Spring’s STOMP client allows you to set headers for handshakes:

WebSocketHttpHeaders handshakeHeaders = new WebSocketHttpHeaders();
handshakeHeaders.add(principalRequestHeader, principalRequestValue);
Copy the code

However, the JavaScript client of SockJS does not support sending Authorization headers using SockJS requests. However, it allows you to send query parameters that can be used to pass tokens. This approach requires writing custom code on the server side that reads the token from the query parameters and validates it. It is particularly important to ensure that tokens are not logged (or well protected) with requests, as this can lead to serious security violations.

5. Backup option of SockJS

Integration with WebSocket may not always be satisfactory. Some browsers (IE 9, for example) do not support Websockets. More importantly, restrictive proxies may make HTTP upgrades impossible, or they may sever connections that have been open for too long. In this case, SockJS comes to the rescue.

SockJS transmissions fall into three categories: WebSocket, HTTP Streaming and HTTP Long Polling. Communication from SockJS starts by sending GET /info to GET basic information from the server. SockJS determines which transport method to use based on the response. The first option is WebSocket. If not, use Streaming if possible. If Streaming is also unavailable, polling is selected as the transport method.

6. Use WebSocket in production

While this setup works, it’s not “optimal.” Spring Boot allows you to use any full messaging system with STOMP (e.g., ActiveMQ, RabbitMQ), and external agents can support more STOMP operations (e.g., confirmation, rental) than the simple agents we used. STOMP Over WebSocket provides information about WebSockets and STOMP protocols. It lists messaging systems that handle STOMP, which might be a better solution for use in production. In particular, because of the large number of requests, the message broker needs to be clustered (Spring’s simple message broker is not suitable for clustering). Then, instead of enabling the simple broker in WebSocketConfig, you need to enable the Stomp broker relay, which forwards messages to and from the external message broker. In summary, external message brokers can help you build more scalable and reliable solutions.

The original link: www.toptal.com/java/stomp-…

Tomasz Dąbrowski

Translator: Emma

Recommend concern public number: the big guy outside the pot

Daily push foreign excellent technical translation articles, encourage to help domestic developers grow better!