Search code examples
springwebsocketkotlinstomp

Spring stomp over websocket SubscribeMapping not working


I'm trying to configure subscription mapping for stomp over websockets in a spring boot application without any luck. I'm fairly certian I have the stomp/websocket stuff configured correctly as I am able to subscribe to topics that are being published to by a kafka consumer, but using the @SubscribeMapping is not working at all.

Here is my controller

@Controller
class TestController {
    @SubscribeMapping("/topic/test")
    fun testMapping(): String {
        return "THIS IS A TEST"
    }
}

And here is my configuration

@Configuration
@EnableWebSocketMessageBroker
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
class WebSocketConfig : AbstractWebSocketMessageBrokerConfigurer() {

    override fun configureMessageBroker(config: MessageBrokerRegistry) {
        config.setApplicationDestinationPrefixes("/app", "/topic")
        config.enableSimpleBroker("/queue", "/topic")
        config.setUserDestinationPrefix("/user")
    }

    override fun registerStompEndpoints(registry:StompEndpointRegistry) {
        registry.addEndpoint("/ws").setAllowedOrigins("*")
    }

    override fun configureClientInboundChannel(registration: ChannelRegistration?) {
        registration?.setInterceptors(object: ChannelInterceptorAdapter() {
            override fun preSend(message: Message<*>, channel: MessageChannel): Message<*> {
                val accessor: StompHeaderAccessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor::class.java)
                if (StompCommand.CONNECT.equals(accessor.command)) {
                    Optional.ofNullable(accessor.getNativeHeader("authorization")).ifPresent {
                        val token = it[0]
                        val keyReader = KeyReader()
                        val creds = Jwts.parser().setSigningKey(keyReader.key).parseClaimsJws(token).body
                        val groups = creds.get("groups", List::class.java)
                        val authorities = groups.map { SimpleGrantedAuthority(it as String) }
                        val authResult = UsernamePasswordAuthenticationToken(creds.subject, token, authorities)
                        SecurityContextHolder.getContext().authentication = authResult
                        accessor.user = authResult
                    }
                }
                return message
            }
        })
    }
}

And then in the UI code, I'm using angular with a stompjs wrapper to subscribe to it like this:

this.stompService.subscribe('/topic/test')
      .map(data => data.body)
      .subscribe(data => console.log(data));

Subscribing like this to topics that I know are emitting data works perfectly but the subscribemapping does nothing. I've also tried adding an event listener to my websocket config to test that the UI is actually sending a subscription event to the back end like this:

    @EventListener
    fun handleSubscribeEvent(event: SessionSubscribeEvent) {
        println("Subscription event: $event")
    }

    @EventListener
    fun handleConnectEvent(event: SessionConnectEvent) {
        println("Connection event: $event")
    }

    @EventListener
    fun handleDisconnectEvent(event: SessionDisconnectEvent) {
        println("Disconnection event: $event")
    }

Adding these event listeners I can see that all the events that I'm expecting from the UI are coming through in the kotlin layer, but my controller method never gets called. Is there anything obvious that I'm missing?


Solution

  • Try the following:

    @Controller
    class TestController {
    
        @SubscribeMapping("/test")
        fun testMapping(): String {
            return "THIS IS A TEST"
        }
    }