He agregado una autenticación personalizada basada en token para mi aplicación Spring-Web y extendiendo lo mismo para Spring WebSocket como se muestra a continuación
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic", "/queue"); config.setApplicationDestinationPrefixes("/app"); config.setUserDestinationPrefix("/user"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/gs-guide-websocket").setAllowedOrigins("*").withSockJS(); } @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 jwtToken = accessor.getFirstNativeHeader("Auth-Token"); if (!StringUtils.isEmpty(jwtToken)) { Authentication auth = tokenService.retrieveUserAuthToken(jwtToken); SecurityContextHolder.getContext().setAuthentication(auth); accessor.setUser(auth); //for Auth-Token '12345token' the user name is 'user1' as auth.getName() returns 'user1' } } return message; } }); } }El código del lado del cliente para conectarse al socket es
var socket = new SockJS('http://localhost:8080/gs-guide-websocket'); stompClient = Stomp.over(socket); stompClient.connect({'Auth-Token': '12345token'}, function (frame) { stompClient.subscribe('/user/queue/greetings', function (greeting) { alert(greeting.body); }); });Y desde mi controlador estoy enviando un mensaje como
messagingTemplate.convertAndSendToUser("user1", "/queue/greetings", "Hi User1"); Para el token de autenticación 12345token , el nombre de usuario es user1 . Pero cuando envío un mensaje al user1 , no se recibe en el extremo del cliente. ¿Hay algo que me estoy perdiendo con esto?
En su controlador Websocket, debe hacer algo como esto:
@Controller public class GreetingController { @Autowired private SimpMessagingTemplate messagingTemplate; @MessageMapping("/hello") public void greeting(Principal principal, HelloMessage message) throws Exception { Greeting greeting = new Greeting(); greeting.setContent("Hello!"); messagingTemplate.convertAndSendToUser(message.getToUser(), "/queue/reply", greeting); } }En el lado del cliente, su usuario debe suscribirse al tema /usuario/cola/respuesta.
También debe agregar algunos prefijos de destino:
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic", "/queue" ,"/user"); config.setApplicationDestinationPrefixes("/app"); config.setUserDestinationPrefix("/user"); } /*...*/ }Cuando su servidor recibe un mensaje en la cola /app/hello, debe enviar un mensaje al usuario en su dto. El usuario debe ser igual al principal del usuario.
Creo que el único problema en su código es que su "/usuario" no está en sus prefijos de destino. Tus mensajes de saludo están bloqueados porque los enviaste en una cola que comienzan con /usuario y este prefijo no está registrado.
Puede consultar las fuentes en git repo: https://github.com/simvetanylen/test-spring-websocket
¡Espero que funcione!
En mi proyecto anterior envié mensajes a un usuario específico; en detalle escribí lo siguiente:
LADO DEL CLIENTE :
function stompConnect(notificationTmpl) { var socket = new SockJS('/comm-svr'); stompClient = Stomp.over(socket); var theUserId stompClient.connect({userId:theUserId}, function (frame) { debug('Connected: ' + frame); stompClient.subscribe('/topic/connect/'+theUserId, function (data) { //Handle data } }); }LADO DEL SERVIDOR
Oyente websocket de primavera:
@Component public class WebSocketSessionListener { private static final Logger logger = LoggerFactory.getLogger(WebSocketSessionListener.class.getName()); private List<String> connectedClientId = new ArrayList<String>(); @EventListener public void connectionEstablished(SessionConnectedEvent sce) { MessageHeaders msgHeaders = sce.getMessage().getHeaders(); Principal princ = (Principal) msgHeaders.get("simpUser"); StompHeaderAccessor sha = StompHeaderAccessor.wrap(sce.getMessage()); List<String> nativeHeaders = sha.getNativeHeader("userId"); if( nativeHeaders != null ) { String userId = nativeHeaders.get(0); connectedClientId.add(userId); if( logger.isDebugEnabled() ) { logger.debug("Connessione websocket stabilita. ID Utente "+userId); } } else { String userId = princ.getName(); connectedClientId.add(userId); if( logger.isDebugEnabled() ) { logger.debug("Connessione websocket stabilita. ID Utente "+userId); } } } @EventListener public void webSockectDisconnect(SessionDisconnectEvent sde) { MessageHeaders msgHeaders = sde.getMessage().getHeaders(); Principal princ = (Principal) msgHeaders.get("simpUser"); StompHeaderAccessor sha = StompHeaderAccessor.wrap(sde.getMessage()); List<String> nativeHeaders = sha.getNativeHeader("userId"); if( nativeHeaders != null ) { String userId = nativeHeaders.get(0); connectedClientId.remove(userId); if( logger.isDebugEnabled() ) { logger.debug("Connessione websocket stabilita. ID Utente "+userId); } } else { String userId = princ.getName(); connectedClientId.remove(userId); if( logger.isDebugEnabled() ) { logger.debug("Connessione websocket stabilita. ID Utente "+userId); } } } public List<String> getConnectedClientId() { return connectedClientId; } public void setConnectedClientId(List<String> connectedClientId) { this.connectedClientId = connectedClientId; } }Remitente del mensaje Spring websocket:
@Autowired private SimpMessagingTemplate msgTmp; private void propagateDvcMsg( WebDeviceStatusInfo device ) { String msg = ""; String userId =((Principal)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getName() msgTmp.convertAndSend("/topic/connect"+userId, msg); }espero que sea util