In my current, I had the opportunity to develop a new application based on Vue.js in the frontend and Spring Boot in the backend. The backend should send updates to the frontend via a WebSocket connection so that users do not need to refresh the website to see the latest information.
The view should show a list of transfers where the user can modify the results by using filters and pagination. As a requirement, each user should receive specific results based on his filters and pagination without broadcasting this to all other connected users. The user does not need to authenticate itself at the backend.
Most tutorials cover either the case of broadcasting messages to all connected users or to send messages to certain authenticated users. In this article, I will demonstrate how to send messages to anonymous users without broadcasting the messages.
The Demo Project
I have a created a demo project to be able to demonstrate the functionality. It is a simple monorepo which contains a backend and a frontend folder.
Backend
The Spring Boot backend was bootstrapped using Spring Initializr where I chose WebSocket
as the only dependency:
Configure Websocket
The next step is to configure the application to use a WebSocket connection. To configure the Spring Boot application I followed this tutorial without the frontend part.
After this tutorial we have a working WebSocket controller that receives and sends messages via a WebSocket connection:
The greeting()
method is called if a message is sent to the /hello
destination. This is ensured by using the @MessageMapping
annotation. The received message is then sent to / topic/greetings
. I have added a simulated delay to simulate any asynchronous operation that could be executed on the server-side in between receiving and sending messages.
In this implementation, all messages are broadcasted to all connected users by using the @SendTo
annotation.
Greeting.java
and HelloMessage.java
are simple Java classes which represent the transferred data objects:
The WebSocket is configured in WebSocketConfig.java
:
In my project, we send updates to the clients via a scheduled interval so I also added this functionality to this demo
project. The first step is to enable scheduling in the Spring Boot application using the @EnableScheduling
annotation:
Next, a Scheduler.java
class handles the scheduled tasks and triggers GreetingService
to send a new message each second:
GreetingsService.java
injects SimpMessagingTemplate
which provides methods to programmatically send WebSocket messages:
convertAndSend
is equivalent to the @SendTo
annotation which was used in the controller to broadcast messages.
Frontend
The frontend was bootstrapped using the Vue CLI:
Next step is to add STOMP.js as npm library which allows us to connect to our backend STOMP broker over WebSocket:
I have created a websocket-service.ts
as Singleton which handles the interaction with this library:
In the constructor, the client configuration is done. If we run the backend locally the WebSocket connection is available at localhost:8080/ws
that's why ws://localhost:8080/ws
is used as broker URL in Vue development mode.
The service provides this public API:
Inside the mounted()
method in App.vue
the service is instantiated:
Received messages are rendered in the template:
At this point we have a running application that can send & broadcast messages via a WebSocket connection:
Prevent Message Broadcasting
As you can see in the video above, each connected user receives the same broadcasted message as we cannot identify certain users. In this chapter, I want to demonstrate how to prevent broadcasting messages to all users without a need for authentication.
The idea is to use UUIDs for each connected client and instead of broadcasting to all users messages are only sent to specific UUIDs.
These steps need to be performed:
- Generate a Spring Security
Principal
name by UUID for each newly connected client by usingDefaultHandshakeHandler
- Store the UUID if a new message is received
- Use
@SendToUser
instead of@SendTo
annotation in the WebSocket controller - Change endpoint in frontend to have the
user
, so/user/topic/greetings
instead of/topic/greetings
;
Let's start by creating a CustomHandshakeHandler.java
which needs to be registered in WebSocketConfig.java
:
Now GreetingController.java
has to be adopted:
GreetingService
needs to be adjusted to be able to store the UUIDs in a list and we change convertAndSend
to convertAndSendToUser
where we iterate over all user names and send message to them:
Finally, the topic endpoint in App.vue
in the frontend code needs to be changed:
Let's see this in action:
Conclusion
Sending WebSocket messages to specific anonymous users is not hard using Spring. You can also extend this mechanism by adding another destination for broadcasted messages. This way, you can send certain messages to specific users and also broadcast messages to every connected user.
Boost Your Productivity By Using The Terminal (iTerm & ZSH)
Using the terminal is one of the biggest productivity boosts you can gain in your daily work as a developer. If you know your shortcuts, you will be way faster than using the mouse. In this article, I want to show you my terminal setup and how I use it on a daily basis. The cover image shows my current setup in action.
How To Generate Angular & Spring Code From OpenAPI Specification
If you are developing the backend and frontend part of an application you know that it can be tricky to keep the data models between the backend & frontend code in sync. Luckily, we can use generators that generate server stubs, models, configuration and more based on a OpenAPI specification.