Spring Boot 4.3.5 WebSocket Chat с авторизацией jwt. В GenericMessage нет пункта назначения

Я пытаюсь реализовать чат 1-1 для мобильного приложения (ionic 3) с серверной частью весенней загрузки. Похоже, возникли некоторые проблемы с конфигурацией.

Невозможно отправить сообщение, вероятно, потому что целевой канал не был создан

Серверная часть:

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)); появляется o.s.m.s.b.DefaultSubscriptionRegistry : No destination in GenericMessage [payload=byte[2], headers={simpMessageType=MESSAGE.... Ошибка.


person user1935987    schedule 20.02.2018    source источник
comment
Здравствуйте, docs.spring .io / spring / docs / 4.0.1.RELEASE / упоминает, как перенаправлять сообщения конкретным пользователям, это не ясно, но вы также можете проверить это stackoverflow.com/questions/30464230/   -  person neo_lestat    schedule 21.02.2018
comment
Не могли бы вы поделиться выводом STOMP в консоли при отправке сообщения и полной ошибки?   -  person Sergi Almar    schedule 06.04.2018


Ответы (1)


https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#websocket-stomp-authentication.

а также

https://stackoverflow.com/a/33962402/8336511

Вот как я решил эту проблему:

Когда пользователь аутентифицируется с помощью Spring Security, модуль WebSocket создает уникальный канал для этого пользователя на основе его Принципала. Пример «/ user / queue / position-updates» переводится в «/ queue / position-updates-user123».

Итак, на стороне клиента все, что мне нужно было сделать, это подписаться на / user / queue / requests.

А на стороне сервера отправляйте сообщения в / user / {username} / queue / requests с помощью convertAndSendToUser (request.getFromUser (), «/ queue / requests», request), а Spring сделает все остальное.

person Alexander Kurutin    schedule 21.02.2018
comment
Я полагаю, что если канал был создан, я должен работать в аннотации контроллера? @SendTo("/topic/chat/{item_id}/chat_with/{buyer_login}") но вместо этого я получаю No destination in GenericMessage, как описано в теме - person user1935987; 22.02.2018