Spring Boot 4.3.5 Чат WebSocket с авторизацией jwt. Нет назначения в GenericMessage
Я пытаюсь реализовать чат 1-1 для мобильного приложения (ионный 3) с back-end с пружинной загрузкой. Похоже, что в некоторых проблемах с конфигурацией.
Не удается отправить сообщение, вероятно, потому, что целевой канал не был создан
Back-End:
ChatController:
@RestController
public class ChatController {
@Autowired
private PrivateChatService privateChatService;
private final static Logger logger = LogManager.getLogger(ChatController.class.getName());
@RequestMapping(value = "/chat/messages/{item_id}/chat_with/{buyer_login}", method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<String> getExistingChatMessages(@PathVariable("item_id") String itemId, @PathVariable("buyer_login") String buyerLogin) {
List<ChatMessage> messages = privateChatService.getExistingChatMessages(itemId, buyerLogin);
logger.info("Here get messages");
return JSONResponseHelper.createResponse(messages, HttpStatus.OK);
}
@MessageMapping("/chat/{item_id}/send")
@SendTo("/topic/chat/{item_id}/chat_with/{buyer_login}")
public ChatMessage send(@Payload ChatMessage message,
@DestinationVariable("item_id") String item_id) throws Exception {
// logger.info(principal.getName());
logger.info(message.toString());
logger.info(item_id);
privateChatService.submitMessage(message);
return message;
}
}
WebSocketConfig:
@Configuration
@EnableWebSocketMessageBroker
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
private final static Logger logger = LogManager.getLogger(WebSocketConfig.class.getName());
@Autowired
private JwtTokenProvider jwtTokenProvider;
@Autowired
private PrivateChatService privateChatService;
private static final String MESSAGE_PREFIX = "/topic";
private static final String END_POINT = "/chat";
private static final String APPLICATION_DESTINATION_PREFIX = "/live";
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
if (registry != null) {
registry.addEndpoint(END_POINT).setAllowedOrigins("*").withSockJS();
}
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
if (registry != null) {
registry.enableSimpleBroker(MESSAGE_PREFIX);
registry.setApplicationDestinationPrefixes(APPLICATION_DESTINATION_PREFIX);
}
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(new ChannelInterceptorAdapter() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
String authToken = accessor.getFirstNativeHeader("Authentication");
String jwt = JwtUtils.resolveToken(authToken);
if (jwtTokenProvider.validateToken(jwt)) {
Authentication authentication = jwtTokenProvider.getAuthentication(jwt);
accessor.setUser(authentication);
String itemId = accessor.getFirstNativeHeader("item_id");
accessor.setDestination("/topic" + privateChatService.getChannelId(itemId, authentication.getName()));
logger.info(accessor.getDestination()); //ex: /topic/chat/3434/chat_with/user3797474342423
}
}
return message;
}
});
}
}
WebSocketSecurityConfig
@Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
@Override
protected boolean sameOriginDisabled() {
return true;
}
}
Мобильный клиент, ng2-stomp-service:
private _initWebsock(auth_token:string, item_id: number) {
let headers: Object = {
Authentication: 'Bearer ${auth_token}',
item_id: item_id
};
this.stomp.configure({
host :this.websocketApi + 'chat',
headers: headers,
queue:{'init':false}
});
console.log("Connecting stomp socket...");
//start connection
this.stomp.startConnect().then(() => {
this.stomp.done('init');
console.log('connected');
//subscribe
this.subscription = this.stomp.subscribe('/chat/${item_id}/', this.socketListener);
});
}
public socketListener = (data) => {
console.log(data)
};
send(msg: ChatMessage, item_id: number){
//send data
console.log(msg);
this.stomp.send('/live/chat/${item_id}/send', {}, JSON.stringify(msg));
}
Проблема 1 (возможно):
В консоли браузера показано, что клиент подписывается на /chat/item_id
вместо /topic/chat/3434/chat_with/user3797474342423
=> кажется, что configureClientInboundChannel
не работает?
Проблема 2:
При попытке выполнить this.stomp.send(
/live/chat/$ {item_id}/send , {}, JSON.stringify(msg));
, получение osmsbDefaultSubscriptionRegistry: No destination in GenericMessage [payload=byte[2], headers={simpMessageType=MESSAGE....
Ошибка.
Ответы
Ответ 1
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#websocket-stomp-authentication
а также
fooobar.com/info/5358494/...
Вот как я решил эту проблему:
Когда пользователь аутентифицируется с помощью Spring Security, модуль WebSocket создает уникальный канал для этого пользователя на основе его Принципала. Пример "/user/queue/position-updates" переводится в "/queue/position-updates-user123"
Поэтому на стороне клиента все, что мне нужно было сделать, было подписано на /user/queue/requests
И на стороне сервера отправьте сообщения /user/{имя_пользователя}/queue/requests с помощью convertAndSendToUser (request.getFromUser(), "/queue/requests", request), а Spring - остальные.