Авторизация spring WebSocket через Bearer в header

Необходимо: Реализовать авторизацию по Bearer, чтобы не авторизованные пользователи не моги получать сообщения.

На данный момент:

  1. Клиент передаёт мне Bearer, но я не могу его получить в beforeHandshake, при этом в postSend я его вижу.
  2. У меня проект на CUBA, пользователи авторизуются в REST через Bearer, хотелось бы используя его же авторизоваться в WebSocket, но не знаю как сделать это через "Authentication authResult = authenticationManager.authenticate(authentication);" выдаёт ошибку (возможно не правильно инициализировал)
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

import java.util.Map;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/user");
        registry.setApplicationDestinationPrefixes("/app/");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry
                .addEndpoint("/ws")
                .setAllowedOrigins("http://localhost:8000")
                .addInterceptors(new HttpSessionHandshakeInterceptor() {
                    @Override
                    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
                        super.beforeHandshake(request, response, wsHandler, attributes);
                        // Получаем заголовок Authorization из запроса
                        HttpHeaders httpHeaders = request.getHeaders();
                        String authorizationHeader = httpHeaders.getFirst(HttpHeaders.AUTHORIZATION);
                        //  Enumeration<String> headers = request.getHeaders("Authorization");
                        // Проверяем наличие Bearer токена
                        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
                            String token = authorizationHeader.substring(7); // Извлекаем токен без "Bearer "
                            // Логика проверки токена здесь
                            // Например Spring Security для аутентификации пользователя
                            return true;
                        } else {
                            // Если отсутствует токен, отклоняем запрос
                            return false;
                        }
                    }
                })
                .withSockJS();
    }
    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(new MyChannelInterceptor());
    }
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;

import java.security.Principal;

public class MyChannelInterceptor implements ChannelInterceptor {

    @Override
    public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
        String token = accessor.getFirstNativeHeader("Authorization");
    }
import { Client } from '@stomp/stompjs'
import SockJS from 'sockjs-client/dist/sockjs'
import { notification } from 'ant-design-vue'

const openNotification = (message) => {
  notification[message.status]({
    message: message.type,
    description: message.messageText,
    placement: 'topRight',
  })
}

let client

const createWebSocket = () => {
  try {
    return new SockJS(import.meta.env.VITE_APP_API_WS)
  }
  catch (error) {
    console.error('Ошибка при создании WebSocket:', error)
  }
}

const subscribeToRoom = async (userId) => {
  try {
    await client.subscribe(`/user/${userId}/terminalmessage`, (message) => {
      openNotification(JSON.parse(message.body))
    })
  }
  catch {
    console.log('Не удалось подписаться на комнату', userId)
  }
}

export function connect(userId, token) {
  console.error('token:', token)

  client = new Client({
    webSocketFactory: createWebSocket,
    connectHeaders: {
      Authorization: `Bearer ${token}`,
    },
    beforeConnect: () => {
      return new Promise((resolve) => {
        resolve()
      })
    },
    onConnect: () => {
      subscribeToRoom(userId)
    },
    onStompError: (frame) => {
      console.error('STOMP error:', frame)
    },
  })

  client?.activate()
}

export function disconnect() {
  if (client?.connected) {
    client.deactivate()
    console.log('Disconnected')
  }
}


Ответы (0 шт):