Spring Boot WebSockets & SimpUserRegistry
Spring Boot 4.0.0 — How to use SimpUserRegistry & StompJs@7.2.0 connectHeaders
Wiki topics:
🔒 · Cybersecurity
Spring Boot WebSockets & SimpUserRegistry
Spring Boot 4.0.0 — How to use SimpUserRegistry & StompJs@7.2.0 connectHeaders.

Youtube: after 05 november 2025
A simple example
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.0-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>willydekeyser</groupId>
<artifactId>websockets_username</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>websockets_username</name>
<description>websockets_username</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>25</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
</project>
WebSocketConfiguration:
package willydekeyser.websockets_username.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat");
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new UserNameChannelInterceptor());
}
}
UserNameChannelInterceptor:
package willydekeyser.websockets_username.config;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.stereotype.Component;
@Component
public class UserNameChannelInterceptor implements ChannelInterceptor {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
assert accessor != null;
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
String login = accessor.getFirstNativeHeader("login");
accessor.setUser(() -> login);
}
return message;
}
}
WebSocketService:
package willydekeyser.websockets_username.service;
import org.springframework.messaging.simp.user.SimpUserRegistry;
import org.springframework.stereotype.Service;
import willydekeyser.websockets_username.model.Session;
import willydekeyser.websockets_username.model.User;
import willydekeyser.websockets_username.model.Users;
import java.util.List;
@Service
public class WebSocketService {
private final SimpUserRegistry simpUserRegistry;
public WebSocketService(SimpUserRegistry simpUserRegistry) {
this.simpUserRegistry = simpUserRegistry;
}
public Users getUsers() {
return new Users(simpUserRegistry.getUsers().size(),
simpUserRegistry.getUsers().stream()
.map(user -> new User(user.getName(),
user.getSessions().size(),
user.getSessions().stream()
.map(session -> new Session(session.getId(),
session.getSubscriptions().size(),
session.getSubscriptions().stream()
.map(subscription ->
subscription.getId() + " - " +
subscription.getDestination())
.toList()))
.toList()
))
.toList()
);
}
}
WebSocketController:
package willydekeyser.websockets_username.controller;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import willydekeyser.websockets_username.model.Users;
import willydekeyser.websockets_username.service.WebSocketService;
@Controller
public class WebSocketController {
private final WebSocketService webSocketService;
public WebSocketController(WebSocketService webSocketService) {
this.webSocketService = webSocketService;
}
@MessageMapping("/message")
@SendTo("/topic/greetings")
String greet(String request) {
return "Hello, " + request;
}
@MessageMapping("/user")
@SendTo("/topic/users")
Users users() {
return webSocketService.getUsers();
}
}
Model:
Users & Sessions
package willydekeyser.websockets_username.model;
import java.util.List;
public record Users(
Integer number_of_users,
List<User> name
) {}
package willydekeyser.websockets_username.model;
import java.util.List;
public record User(
String user,
Integer number_of_sessions,
List<Session> sessions) {
}
package willydekeyser.websockets_username.model;
import java.util.List;
public record Session(
String session,
Integer number_of_subscriptions,
List<String> subscriptions
) {
}
main.js:
'use strict';
let userName;
let url;
let client;
let inputUsername
let buttonConnect;
let buttonDisConnect;
let users_log;
let messages_log;
function connectWebSocket(username) {
url = "ws://localhost:8080/chat";
client = new StompJs.Client({
brokerURL: url,
connectHeaders: {
login: username,
},
debug: (str) => console.log(str),
})
client.onConnect = () => {
client.subscribe('/topic/greetings', (greeting) => {
printMessage(messages_log, greeting.body);
});
client.subscribe('/topic/users', (user) => {
printMessage(users_log, user.body);
}, {});
client.publish({
destination: '/app/message',
body: 'Spring Boot Tutorial',
});
client.publish({
destination: '/app/user',
});
};
client.onWebSocketError = (error) => {
console.error('Error with websocket', error);
};
client.onStompError = (frame) => {
console.error('Broker reported error: ' + frame.headers['message']);
console.error('Additional details: ' + frame.body);
};
}
window.addEventListener('DOMContentLoaded', () => {
buttonConnect = document.getElementById("connect");
buttonDisConnect = document.getElementById("disconnect");
inputUsername = document.getElementById("username");
users_log = document.getElementById("users_log");
messages_log = document.getElementById("messages_log");
buttonConnect.disabled = false;
buttonDisConnect.disabled = true;
})
function connect() {
if (inputUsername.value === '') {
alert("UserName is empty");
return;
}
clearMessage(users_log);
clearMessage(messages_log);
buttonConnect.disabled = true;
buttonDisConnect.disabled = false;
userName = inputUsername.value;
connectWebSocket(userName);
client.activate();
}
function disconnect() {
client.deactivate();
buttonConnect.disabled = false;
buttonDisConnect.disabled = true;
clearMessage(users_log);
clearMessage(messages_log);
}
function printMessage(textId, data) {
textId.innerHTML += data + "<br/>";
}
function clearMessage(textId) {
console.log('clearMessage', textId);
textId.innerHTML = "";
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Spring Boot Tutorial</title>
<script src="https://cdn.jsdelivr.net/npm/@stomp/stompjs@7.2.0/bundles/stomp.umd.min.js"></script>
<script src="main.js"></script>
</head>
<body>
<h1>Spring Boot Tutorial</h1>
<label for="username">Username: </label><input type="text" id="username" placeholder="Username here..." size="50" onfocus=this.value='' /><br/><br/>
<input id="connect" type="button" onclick="connect()" value="Connect" />
<input id="disconnect" type="button" onclick="disconnect()" value="Disconnect" /><br/><br/>
<p>Users: <br/> <span id="users_log"></span></p>
<p>Messages:<br/> <span id="messages_log"></span></p>
</body>
</html>
GitHub: https://github.com/wdkeyser02/websockets_username
Willy De Keyser
메타데이터
- post_id
- 3ef7f6205301
- slug
- spring-boot-websockets-simpuserregistry-3ef7f6205301
- url
- https://medium.com/@wdkeyser/spring-boot-websockets-simpuserregistry-3ef7f6205301
- canonical_url
- https://medium.com/@wdkeyser/spring-boot-websockets-simpuserregistry-3ef7f6205301
- author_url
- https://medium.com/@wdkeyser
- status
- ok
- fetched_at
- 2026-07-16 14:59:26