2015-11-16 16 views
12

Używam Springa STOMP nad implementacją WebSocket z pełnoprawnym brokerem ActiveMQ. Kiedy użytkownicy mają SUBSCRIBE tematu, istnieje pewna logika uprawnień, którą muszą przejść, zanim zostanie pomyślnie zasubskrybowana. Używam ChannelInterceptor zastosować logikę uprawnień, jak skonfigurowany poniżej:Jak wysłać komunikat ERROR do klientów STOMP za pomocą Spring WebSocket?

WebSocketConfig.java:

@EnableWebSocketMessageBroker 
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { 

    @Override 
    public void registerStompEndpoints(StompEndpointRegistry registry) { 
    registry.addEndpoint("/stomp") 
     .setAllowedOrigins("*") 
     .withSockJS(); 
    } 

    @Override 
    public void configureMessageBroker(MessageBrokerRegistry registry) { 
    registry.enableStompBrokerRelay("/topic", "/queue") 
     .setRelayHost("relayhost.mydomain.com") 
     .setRelayPort(61613); 
    } 

    @Override 
    public void configureClientInboundChannel(ChannelRegistration registration) { 
    registration.setInterceptors(new MySubscriptionInterceptor()); 
    } 


} 

WebSocketSecurityConfig.java:

public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer { 

    @Override 
    protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) { 
    messages 
     .simpSubscribeDestMatchers("/stomp/**").authenticated() 
     .simpSubscribeDestMatchers("/user/queue/errors").authenticated() 
     .anyMessage().denyAll(); 
    } 

} 

MySubscriptionInterceptor.java:

public class MySubscriptionInterceptor extends ChannelInterceptorAdapter { 

    @Override 
    public Message<?> preSend(Message<?> message, MessageChannel channel) { 

    StompHeaderAccessor headerAccessor= StompHeaderAccessor.wrap(message); 
    Principal principal = headerAccessor.getUser(); 

    if (StompCommand.SUBSCRIBE.equals(headerAccessor.getCommand())) { 
     checkPermissions(principal); 
    } 

    return message; 
    } 

    private void checkPermissions(Principal principal) { 
    // apply permissions logic 
    // throw Exception permissions not sufficient 
    } 
} 

Gdy klienci, którzy nie mają odpowiednich uprawnień, próbują zasubskrybować ograniczony temat, nigdy nie otrzymują wiadomości od tematu, ale nie są również powiadamiani o zgłoszonym wyjątku, który odrzucił ich subskrypcję. Zamiast tego klient otrzymuje niedziałającą subskrypcję, o której broker ActiveMQ nic nie wie. (Normalne, odpowiednio dozwolone interakcje klienta z punktem końcowym STOMP i tematy działają zgodnie z oczekiwaniami).

Próbowałem subskrybować users/{subscribingUsername}/queue/errors i po prostu users/queue/errors z moim klientem testowym Java po pomyślnym podłączeniu, ale do tej pory miałem nie można uzyskać komunikatu o błędzie dotyczącego wyjątku subskrypcji z serwera dostarczonego do klienta. Jest to oczywiście mniej niż idealne, ponieważ klienci nigdy nie otrzymują powiadomienia, że ​​odmówiono im dostępu.

Odpowiedz

5

Nie można po prostu wyrzucić wyjątek od MySubscriptionInterceptor na clientInboundChannel, bo ostatnia jest ExecutorSubscribableChannel, dlatego jest async a wszelkie wyjątki od tych wątków są skończyć w dziennikach z każdym ponownym rzucał do rozmówcy - StompSubProtocolHandler.handleMessageFromClient .

Ale co można zrobić, istnieje coś takiego jak clientOutboundChannel i używać go tak:

StompHeaderAccessor headerAccessor = StompHeaderAccessor.create(StompCommand.ERROR); 
headerAccessor.setMessage(error.getMessage()); 

clientOutboundChannel.send(MessageBuilder.createMessage(new byte[0], headerAccessor.getMessageHeaders())); 

Inną opcją do rozważenia jest mapowanie Adnotacja:

@SubscribeMapping("/foo") 
    public void handleWithError() { 
     throw new IllegalArgumentException("Bad input"); 
    } 

    @MessageExceptionHandler 
    @SendToUser("/queue/error") 
    public String handleException(IllegalArgumentException ex) { 
     return "Got error: " + ex.getMessage(); 
    } 
+0

Dzięki za tym @Artem, dałem go próba. Niestety, gdy wyjątek jest zgłaszany w metodzie '@ SubscribeMapping' i obsługiwany przez metodę' @ MessageExceptionHandler', komunikat jest wysyłany do kolejki błędów klienta, ALE ich subskrypcja jest nadal przekazywana do brokera, a klient nadal otrzymuje kolejne wiadomości z tematu. – hartz89

+1

Spróbuj więc użyć bezpośredniego 'wyślij' do' clientOutboundChannel' z tego '@ MessageExceptionHandler'. –

+0

@ArtemBilan możesz wyjaśnić, jak uzyskać OutboundChannel? Zadaje pytanie: http://stackoverflow.com/questions/39641477/send-stomp-error-from-spring-websocket-program –

Powiązane problemy