Spring Веб-узлы @SendToUser без входа?

У меня есть простое приложение spring с функциональностью websocket, и все работает до сих пор. Теперь я хочу отправить сообщение с моего сервера на конкретный клиент, используя аннотацию @SendToUser. Это дает мне ошибку "Игнорирование сообщения, доступной информации о принципе недоступности". Я понимаю, что у меня нет логина на моем сервере, поэтому каждый пользователь "анонимный" и не имеет принципала (на данный момент я не использую spring). Но каждый пользователь имеет идентификатор сеанса. Невозможно ли использовать идентификатор сеанса, чтобы различать пользователей? Как я могу достичь этого, чтобы мои пользователи получили принципал, который соответствует идентификатору сеанса?

Ответ 1

Я думаю, что решение может состоять в том, чтобы избежать использования @SendToUser и использовать raw SimpMessagingTemplate и отправлять сообщения адресату, который вы контролируете для открытых сеансов.

Например, предполагая, что у вас есть определенный идентификатор для нового сеанса websocket, вы можете подписаться на очередь с этим идентификатором в имени очереди:

stomp.subscribe("/queue/chats" + "-" + mycustomidentifier, onmessage);

Теперь, на стороне слушателя Spring, вы можете направить свои ответы с помощью SimpMessagingTemplate:

@Controller
public class MyController {


    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    @MessageMapping("/chats")
    public void handleChat(@Payload ChatMessage message) {
        this.simpMessagingTemplate.convertAndSend("/queue/chats-" + "mycustomidentifier", "[" + getTimestamp() + "]:" + message.getMessage());
    }
....

Ответ 2

Используйте @SendToUser и добавьте "/user/" перед очередью при подписке (только абонентская сторона). Мастер отдыха работает: -)

Вместо

Java Server: @SendTo("/topic/showResult")

и

JS Client: stompClient.subscribe('/topic/showResult', function(calResult){  ....

использовать:

Java Server: @SentToUser("/topic/showResult")

и

JS Client: stompClient.subscribe('/user/topic/showResult', function(calResult){ ....

Ответ 3

Основываясь на ответе Biju и используя сгенерированный идентификатор сеанса (спасибо, mariusz2108 в его ответ на аналогичный вопрос), вот что сработало для меня (на основе канонический пример из Spring)

Клиент SpringFramework:

private SimpMessagingTemplate template;

@Autowired
public GreetingController(SimpMessagingTemplate template) {
    this.template = template;
}

@MessageMapping("/hello")
public void greeting(HelloMessage message, @Header("simpSessionId") String sessionId) throws Exception {
    template.convertAndSend("/queue/greeting-"+sessionId, new Greeting("Hello, " + message.getName()));
}

Клиент JavaScript:

function connect() {
    var socket = new SockJS('/gs-guide-websocket');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function (frame) {
        var sessionId = /\/([^\/]+)\/websocket/.exec(socket._transport.url)[1];
        console.log("connected, session id: " + sessionId);
        stompClient.subscribe('/queue/greeting-'+sessionId, function (greeting) {
            showGreeting(JSON.parse(greeting.body).content);
        });
    });
}

Вместо идентификатора сеанса Stomp вы можете использовать идентификатор сеанса веб-контейнера (например, JSESSIONID), но теперь этот файл cookie не доступен по умолчанию из JavaScript (для Tomcat) это более сложная перспектива.