Access JWT token in Spring Boot with Stomp

Viewed 785

I have followed this guide to set up ng2-stompjs, but I have problems with on my Spring boot backend, as I use JWT tokens.

Firstly, the stomp service did not want to connect because of my JWT security, so I added this to the Security Configuration:

SecurityConfiguration:

http.authorizeRequests().antMatchers("/websockets").permitAll();

WebSocketConfiguration

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/websockets").setAllowedOrigins("*");
}

My frontend stomp configuration:

import { InjectableRxStompConfig } from '@stomp/ng2-stompjs';

export const myRxStompConfig: InjectableRxStompConfig = {
  // Which server?
  brokerURL: 'ws:/localhost:3000/websockets',

  // How often to heartbeat?
  // Interval in milliseconds, set to 0 to disable
  heartbeatIncoming: 0, // Typical value 0 - disabled
  heartbeatOutgoing: 20000, // Typical value 20000 - every 20 seconds

  // Wait in milliseconds before attempting auto reconnect
  // Set to 0 to disable
  // Typical value 500 (500 milli seconds)
  reconnectDelay: 200,

  // Will log diagnostics on console
  // It can be quite verbose, not recommended in production
  // Skip this key to stop logging to console
  debug: (msg: string): void => {
    console.log(new Date(), msg);
  },

  beforeConnect: (stompClient: any): Promise<void> => {
    const token = localStorage.getItem('JWT_TOKEN');
    return new Promise<void>((resolve, _) => {
      stompClient.connectHeaders = {
        Authorization: 'Bearer ' + token, //also tried x-auth-token
      };
      resolve();
    });
  },
};

And the component where I use sockets (note: disregard if the destinations are incorrect, I was only testing):

ngOnInit(): void {
  this.rxStompService.watch('/topic/test').subscribe((message: Message) => {
    console.log(message.body);
  });
}

public test(): void {
  this.rxStompService.publish({ destination: '/hello' });
}

Is there a way to actually append the token? Note, I am using refresh tokens, so every 10 minutes a new access token is generated, and a new access token has to be used for every request.

And second, how do i extract the token in the spring endpoint? In other ones, i would use HttpServletRequest and extract the Authorization header, but how do I do this with sockets? I need this, because I need to use the username, from the person who made the request (a simple workaroud would be to pass the username in the body, but I want to have it secured with JWT, if possible).

@MessageMapping("/hello")
@SendTo("/topic/test")
public void createGameMP(HttpServletRequest request) {
    log.info("HI");
    log.info(request.getHeader(AUTHORIZATION).substring("Bearer ".length())); // obvious error
}
1 Answers

you can use Interceptor intercept websocket request and set token in request.

  1. Define custom HandshakeInterceptor
public class MyHandshakeInterceptor implements HandshakeInterceptor {

    private final static String X_AUTH_TOKEN = "x-auth-token";

    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes)
            throws Exception {
        
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
            HttpServletRequest httpServletRequest = servletRequest.getServletRequest();
            String token = httpServletRequest.getParameter(X_AUTH_TOKEN);
            if (null == token) {
                httpServletRequest.setParameter(X_AUTH_TOKEN, "xxxxx");//todo token value
            }
        }
        return true;
    }
}
  1. configuration
@Bean
public HandshakeInterceptor getHandshakeInterceptor() {
    return new MyHandshakeInterceptor();
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/wse").setAllowedOrigins("*").setInterceptors(myHandshakeInterceptor);
    registry.addEndpoint("/wss").setAllowedOrigins("*").setInterceptors(myHandshakeInterceptor);
}
Related