Working with WebSockets

1. Adding WebSocket Dependencies

Example: WebSocket dependency

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

2. Configuring WebSocket Message Broker

Example: Configure STOMP broker over SockJS

@Configuration
@EnableWebSocketMessageBroker
public class WsConfig implements WebSocketMessageBrokerConfigurer {
  @Override public void registerStompEndpoints(StompEndpointRegistry r) {
    r.addEndpoint("/ws").setAllowedOriginPatterns("*").withSockJS();
  }
  @Override public void configureMessageBroker(MessageBrokerRegistry r) {
    r.enableSimpleBroker("/topic", "/queue");
    r.setApplicationDestinationPrefixes("/app");
    r.setUserDestinationPrefix("/user");
  }
}

3. Registering STOMP Endpoints

Method Use
addEndpoint("/path") STOMP over WebSocket URL
withSockJS() Fallback for older browsers
setAllowedOriginPatterns(...) CORS
setHandshakeHandler(...) Custom user/principal

4. Configuring Message Broker

Broker Use
Simple in-memory Single-node dev/small apps
External (RabbitMQ STOMP, ActiveMQ) Multi-node clustering

Example: External RabbitMQ STOMP relay

r.enableStompBrokerRelay("/topic","/queue")
 .setRelayHost("rabbitmq").setRelayPort(61613)
 .setClientLogin("guest").setClientPasscode("guest");

5. Creating Message Handlers (@MessageMapping)

Example: Chat message handler with @MessageMapping

@Controller
public class ChatController {
  @MessageMapping("/chat") // client sends to /app/chat
  @SendTo("/topic/messages")
  public ChatMessage on(ChatMessage in, Principal user) {
    return new ChatMessage(user.getName(), in.text(), Instant.now());
  }
}

6. Sending Messages to Clients (@SendTo)

Annotation Behavior
@SendTo("/topic/...") Broadcast destination
@SendToUser("/queue/...") Per-user queue
@SubscribeMapping("/init") Reply on subscribe

7. Using SimpMessagingTemplate for Broadcasting

Example: Broadcast and user-specific messaging

@Service
public class Notifier {
  private final SimpMessagingTemplate template;
  public Notifier(SimpMessagingTemplate template) { this.template = template; }
  public void notifyOrder(OrderPlaced o) {
    template.convertAndSend("/topic/orders", o);
    template.convertAndSendToUser("alice", "/queue/alerts", o);
  }
}

8. Handling User-Specific Messages (@SendToUser)

Example: Send reply to user private queue

@MessageMapping("/private")
@SendToUser("/queue/private")
public Reply privateMsg(Message msg, Principal user) {
  return new Reply("Hello " + user.getName());
}

9. Implementing WebSocket Interceptors

Example: Authenticate STOMP CONNECT with token

@Override
public void configureClientInboundChannel(ChannelRegistration r) {
  r.interceptors(new ChannelInterceptor() {
    @Override public Message<?> preSend(Message<?> m, MessageChannel ch) {
      StompHeaderAccessor acc = MessageHeaderAccessor.getAccessor(m, StompHeaderAccessor.class);
      if (StompCommand.CONNECT.equals(acc.getCommand())) {
        String token = acc.getFirstNativeHeader("Authorization");
        // validate; set acc.setUser(...)
      }
      return m;
    }
  });
}

10. Handling WebSocket Events

Event Description
SessionConnectEvent STOMP CONNECT received
SessionConnectedEvent CONNECT acked
SessionSubscribeEvent Client subscribed
SessionUnsubscribeEvent Unsubscribed
SessionDisconnectEvent Closed

11. Securing WebSocket Endpoints

Example: WebSocket authorization by destination

@Configuration
@EnableWebSocketSecurity
public class WsSecConfig {
  @Bean
  AuthorizationManager<Message<?>> messageAuthz(MessageMatcherDelegatingAuthorizationManager.Builder b) {
    return b.simpDestMatchers("/app/admin/**").hasRole("ADMIN")
            .anyMessage().authenticated()
            .build();
  }
}