20

I'm using stomp.js over SockJS in my javascript client. I'm connecting to websocket using

stompClient.connect({}, function (frame) {

stomp over sockJS connection has 2 http requests:

  1. request to /info
  2. http upgrade request

the client sends all cookies. I would like to also send custom headers (e.g. XSRF header) but didn't find a way to do that. Will appreciate any help.

3
  • 2
    Looks like it is not possible to pass custom HTTP headers to the WebSocket handshake requests. Sep 2, 2014 at 6:29
  • It's possible, but i don't know how to do it with stomp client.
    – Ruslan
    Mar 24, 2015 at 13:06
  • Have you solved your problem? I'm experiencing a similar problem and I'd like to hear your solution, if you have one. Dec 4, 2015 at 14:06

5 Answers 5

9

@Rohitdev So basically you can't send any HTTP headers using stompClient, because STOMP is layer over websockets, and only when websockets handshake happen we have possibility to send custom headers. So only SockJS can send this headers, but for some reasons don't do this: https://github.com/sockjs/sockjs-client/issues/196

1
9

Custom headers:

stompClient.connect({token: "ABC123"}, function(frame) { ... code ...});

Without Custom headers:

stompClient.connect({}, function(frame) { ... code ...});

In Javascript, you can extract an STOMP header using:

  username = frame.headers['user-name'];

In the server side, if you are using Spring Framework you can implementa an Interceptor to copy the HTTP parmeters to WebSockets STOMP headers.

public class HttpSessionHandshakeInterceptor_personalised implements HandshakeInterceptor {

    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
            WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {


        // Set ip attribute to WebSocket session
        attributes.put("ip", request.getRemoteAddress());

        // ============================================= CODIGO PERSONAL
        ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
        HttpServletRequest httpServletRequest = servletRequest.getServletRequest();
//        httpServletRequest.getCookies();
//        httpServletRequest.getParameter("inquiryId");
//        httpServletRequest.getRemoteUser();

         String token = httpServletRequest.getParameter("token");


      ...
    }
}

And for send messages without STOMP parameters:

function sendMessage() {
     var from = document.getElementById('from').value;
     var text = document.getElementById('text').value;
            stompClient.send("/app/chatchannel", {},
               JSON.stringify({'from':from, 'text':text}));
}

and here you are passing parameters into the STOMP headers.

function sendMessage() {
     var from = document.getElementById('from').value;
     var text = document.getElementById('text').value;
            stompClient.send("/app/chatchannel", {'token':'AA123'},
               JSON.stringify({'from':from, 'text':text}));
}
5
  • 2
    first of all it does not work in spring part. second: see @IRus answer for why Apr 7, 2017 at 11:28
  • But I can send parameters with SockJs, is workig with my implementation.
    – Sergio
    Aug 31, 2017 at 18:10
  • 2
    I don't see the header no matter how many times I cast the ServerHttpRequest
    – nurettin
    Jan 3, 2019 at 11:03
  • 2
    I confirm, the spring part does not work, there is no "token" parameter available
    – Migs
    Feb 14, 2020 at 8:42
  • so 'custom headers' seem to be not headers at all but request parameters judging by your processing of them? where did you get that code? Jun 15, 2022 at 14:57
2

SockJS JavaScript client does not support sending authorization header with a SockJS request.

Spring Java’s STOMP client allows to set headers for the handshake:

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

Additional information: https://www.toptal.com/java/stomp-spring-boot-websocket

And you can find a lot of infоrmation about this point on Spring documentation: https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#websocket-stomp-authentication

Short conclusion: for applications using cookies, integration is very good (Spring Security and other).

For applications using JWT, possible options are:

1. Add as a request parameter and process in the implementation of DefaultHandshakeHandler

var socket = new SockJS('/our-websocket?query=token_2222');

2. OR add directly to the STOMP header of the message:

//add

var headers = {
        login: 'mylogin',
        passcode: 'mypasscode',
        // additional header
        'client-id': 'my-client-id'
    };

    stompClient.connect(headers, function (frame) {}

//Place of processing

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(final MessageBrokerRegistry registry) {

        registry.enableStompBrokerRelay("/topic")
                .setRelayHost("127.0.0.1")
                .setRelayPort(61613) //rabbitmq-plugins enable rabbitmq_stomp ; docker exec -it ID bash
                .setClientLogin("guest")
                .setClientPasscode("guest")
                .setUserRegistryBroadcast("/topic/registry") //позволяет отправлять сообщеня всем приватные сообщения всем юзерам
        ;
        registry.setApplicationDestinationPrefixes("/ws");

    }


    @Override
    public void registerStompEndpoints(final StompEndpointRegistry registry) {
        registry.addEndpoint("/our-websocket")
                .setHandshakeHandler(new UserHandshakeHandler())
                .setAllowedOriginPatterns("*")
                .withSockJS()
        ;
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(new ChannelInterceptor() {


            @Override
            public Message<?> preSend(Message<?> message, MessageChannel channel) {


                StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
                if (StompCommand.CONNECT.equals(accessor.getCommand())) {
// 1) Perform authentication here using standard authentication providers (managers).

//2) Install the user in case of successful authentication or throw an error
                    accessor.setUser(new UserPrincipal("TEST USER 2"));
                }
                return message;
            }
        });
    }
}
 
1

Use @Header(name = "token") annotation inside the method if you are using Spring boot at the server.

Usage -

@Controller
public class SocketController {

    static final String token = "1234";

    @MessageMapping("/send")
    @SendTo("/receive/changes")
    public Object notify(MessageModel message, @Header(name = "token") String header)throws Exception {
        if(!header.equals(token)) {
            // return when headers do not match
            return("Unauthorized");
        }
        // return the model object with associated sent message
        return new MessageModel(message.getMessage());
    } 
}

You should have a MessageModel class with message variable and required getters, setters and contructor.

In frontend use Stomp

Usage -

function sendMessage() {
     var text = document.getElementById('text').value;
            stompClient.send("/send/message", {'token':'1234'},
               JSON.stringify({'message':text}));
}

To add more security you an use CORS in Spring

0

You must use query as parameter instead to use Authorization in Header. (?query=token_2222) example: var socket = new SockJS('/ws?query=token_2222'); then read it in HandshakeInterceptor as Sergio wrote

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.