STOMP-протокол определяет механизм установления соединения через специальный управляющий кадр CONNECT, в котором и передаются данные аутентификации. В контексте STOMP.js именно этот этап становится ключевой точкой интеграции с системой авторизации, поскольку WebSocket сам по себе не предоставляет унифицированного механизма передачи учетных данных после установки соединения.
CONNECT-кадр формируется клиентом и отправляется брокеру сразу после установления транспортного канала (WebSocket или SockJS). Внутри этого кадра находятся заголовки, которые и используются для передачи идентификационной информации:
Эти заголовки не являются HTTP-заголовками в классическом смысле. Они существуют исключительно в рамках STOMP-сессии и интерпретируются серверной частью брокера.
Браузерный WebSocket API не позволяет динамически управлять HTTP-заголовками после выполнения handshake-запроса. Это означает, что стандартные механизмы вроде Authorization header в HTTP-запросе недоступны на этапе установки WebSocket-соединения.
STOMP.js решает эту проблему переносом аутентификации на уровень протокольного сообщения CONNECT. В результате:
В STOMP.js учетные данные обычно передаются через параметры подключения клиента:
import { Client } from '@stomp/stompjs';
const client = new Client({
brokerURL: 'ws://localhost:8080/ws',
connectHeaders: {
login: 'user1',
passcode: 'password123'
},
onConnect: () => {
console.log('Connected');
}
});
client.activate();
В этом случае STOMP.js автоматически формирует CONNECT-кадр следующего вида:
CONNECT
login:user1
passcode:password123
\0
Брокер, например RabbitMQ или Spring STOMP endpoint, обрабатывает эти заголовки в момент аутентификации сессии.
В современных архитектурах прямое использование пар логин/пароль встречается реже. Более распространена схема с токенами, чаще всего JWT. В этом случае учетные данные передаются через пользовательский заголовок:
const client = new Client({
brokerURL: 'ws://localhost:8080/ws',
connectHeaders: {
Authorization: `Bearer ${token}`
}
});
Такой подход позволяет:
На серверной стороне токен извлекается из CONNECT-кадра и проходит проверку до завершения установки STOMP-сессии.
При использовании SockJS появляется дополнительный уровень абстракции. Фактическое WebSocket-соединение может заменяться XHR-streaming или long polling, но STOMP-логика остается неизменной.
import SockJS from 'sockjs-client';
import { Client } from '@stomp/stompjs';
const client = new Client({
webSocketFactory: () => new SockJS('http://localhost:8080/ws'),
connectHeaders: {
Authorization: `Bearer ${token}`
}
});
В этом случае учетные данные все так же передаются внутри STOMP CONNECT, а не в HTTP handshake, что сохраняет единообразие модели безопасности.
На стороне Spring-based брокеров обработка CONNECT-кадра может быть интегрирована в SecurityContext через перехватчик канала:
@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())) {
String auth = accessor.getFirstNativeHeader("Authorization");
// проверка токена
}
return message;
}
});
}
На этом этапе соединение еще не считается установленным, что позволяет отклонить клиента до подписки на топики и получения сообщений.
Передача учетных данных через CONNECT-кадр решает только задачу аутентификации. Авторизация выполняется на уровне команд SUBSCRIBE и SEND.
Типичный сценарий:
Таким образом, учетные данные используются только на первом этапе, а дальнейшее поведение определяется контекстом сессии.
STOMP.js поддерживает автоматическое переподключение, при котором CONNECT-кадр формируется повторно. Это означает, что все учетные данные должны быть доступны в момент реконнекта:
const client = new Client({
brokerURL: 'ws://localhost:8080/ws',
reconnectDelay: 5000,
connectHeaders: {
Authorization: `Bearer ${token}`
}
});
При потере соединения библиотека заново отправляет CONNECT с теми же заголовками. Это накладывает требования на:
В сценариях с истекающими токенами требуется обновление connectHeaders до переподключения:
client.beforeConnect = async () => {
const newToken = await refreshToken();
client.connectHeaders.Authorization = `Bearer ${newToken}`;
};
В некоторых реализациях используется внешнее управление состоянием, при котором STOMP-клиент не хранит токен, а получает его из функции-источника перед каждым CONNECT.
Если сервер отклоняет учетные данные, он отправляет STOMP ERROR кадр и закрывает соединение. STOMP.js обрабатывает это через callback:
const client = new Client({
onStompError: (frame) => {
console.error('Auth error:', frame.headers['message']);
}
});
Типичные причины отказа:
Передача учетных данных через STOMP требует учета особенностей транспортного уровня:
Особенно критично исключить передачу паролей в открытом виде в production-среде. В современных системах предпочтение отдается токенам с ограниченным временем жизни и возможностью отзыва.
В архитектурах с отдельным identity provider STOMP CONNECT часто становится точкой интеграции:
Это позволяет унифицировать безопасность между REST API и WebSocket-каналом без дублирования логики аутентификации.