Estoy tratando de implementar una aplicación simple de websockets que pueda enviar mensajes desde un punto final a una sesión específica establecida en otro lugar. Hasta ahora, he podido usar la anotación @SendToUser() para que un cliente se suscriba a un canal (como se explica en esta pregunta: Spring Websockets @SendToUser sin iniciar sesión).
Sin embargo, ahora quiero crear un punto final separado que, cuando se llame, busque un usuario asociado con los datos pasados a este punto final y envíe un mensaje a ese usuario con respecto a estos datos.
Sin embargo, no he podido determinar con precisión cómo hacerlo para poder llamar al comando SimpMessagingTemplate convertAndSendToUser() porque mis usuarios no tienen un Principal (no estoy usando Spring Security).
Pude obtener el simpSessionId de MessageHeaders pasado a un punto final de @MessageMapping, pero ahora no puedo entender cómo usar el simpSessionId para enviar información desde diferentes partes de mi aplicación.
Investigué un poco sobre la anulación del método determineUser() de DefaultHandshakeHandler y la asignación de un UUID generado aleatoriamente como nombre de usuario a un usuario después de un protocolo de enlace websocket exitoso (como se describe en una respuesta a esta pregunta: Cómo responder a un usuario no autenticado en Spring 4 ¿STOMP sobre la configuración de WebSocket? ), pero dado que el principal que aparece es nulo, no estoy exactamente seguro de cómo generar uno correctamente y asignarlo al Principal para usarlo con la aplicación.
Básicamente, necesito la capacidad de tener usuarios anónimos y enviarles mensajes desde diferentes partes de la aplicación después de que hayan creado una conexión websocket.
Entonces, para cualquier otra persona que enfrente un problema similar, implementé mi propia clase Principal:
package hello; import java.security.Principal; import java.util.Objects; public class AnonymousPrincipal implements Principal { private String name; @Override public String getName() { // TODO Auto-generated method stub return name; } public void setName(String name) { this.name = name; } @Override public boolean equals(Object another) { if (!(another instanceof Principal)) return false; Principal principal = (Principal) another; return principal.getName() == this.name; } @Override public int hashCode() { return Objects.hash(name); } }Luego, implementé mi propia versión de DefaultHandshakeHandler:
package hello; import java.security.Principal; import java.util.Map; import java.util.UUID; import org.springframework.http.server.ServerHttpRequest; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.support.DefaultHandshakeHandler; public class CustomHandshakeHandler extends DefaultHandshakeHandler { @Override protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) { Principal principal = request.getPrincipal(); if (principal == null) { principal = new AnonymousPrincipal(); String uniqueName = UUID.randomUUID().toString(); ((AnonymousPrincipal) principal).setName(uniqueName); } return principal; } }Ahora, a una sesión de websocket se le asigna este principal cuando se lleva a cabo el protocolo de enlace, por lo que si el usuario es anónimo, se le asigna un principal anónimo que me permitirá almacenar su nombre (UUID generado) para su uso posterior en otras partes de la aplicación.