【问题标题】:Stomp client doesn't receive message on user destination?Stomp 客户端在用户目的地没有收到消息?
【发布时间】:2021-04-08 07:11:28
【问题描述】:

我遇到的问题是客户端未收到发送到用户目的地的 stomp 消息。让我解释一下:

在 WebSocketMessageBrokerConfigurer 中:

public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/secured/topic", "/secured/user/queue");
config.setUserDestinationPrefix("/secured/user");
..

在我的 stompjs 客户端(以“admin”身份登录):

stompClient.subscribe('/secured/user/queue/notifications'
                        , function (output) {
                            console.log(output);
                        });

在日志中:

Processing SUBSCRIBE /secured/user/queue/notifications id=sub-0 session=d74f49b3-bb63-580f-b862-81647cc712b3

以及发送消息的java代码:

simpMessagingTemplate.convertAndSendToUser(
      "admin", "/secured/user/queue/notifications", out);

导致日志:

Processing MESSAGE destination=/secured/user/queue/notifications-userd74f49b3-bb63-580f-b862-81647cc712b3 session=null payload={"from":"server","text":"hello","recipient":"admin","time":"13:29:10"}
      - 

但控制台日志中没有打印任何消息。

如您所见,订阅和发送步骤中的 sessionid 是相同的。但我不明白为什么客户没有收到消息。请注意,没有用户目的地的消息正常工作。

谁能帮我一把?

【问题讨论】:

    标签: java spring spring-websocket stomp


    【解决方案1】:

    我尝试了很多变化,但我不确定它为什么会起作用,但我不抱怨。我做了什么:

    在 configureMessageBroker (WebSocketMessageBrokerConfigurer) 中(这真的让我很困惑,违背了我阅读的所有示例):

    config.enableSimpleBroker("/secured/topic", "/secured/queue", "/secured/user");
    

    convertAndSendToUser(“/secured/user”+用户名由convertAndSendToUser添加):

    simpMessagingTemplate.convertAndSendToUser(   "admin", "/queue/notifications", out);
    

    和我在客户端上的订阅(不太高兴在这里添加用户名..):

    stompClient.subscribe("/secured/user/"+userName+"/queue/notifications"
                            , function (output) {
    

    但它有效;-)

    [编辑] 跟踪日志可以更轻松地了解正在发生的事情:

      <Logger name="org.springframework.messaging" level="trace" additivity="false">
                <AppenderRef ref="Console"/>
            </Logger>
    

    【讨论】:

      【解决方案2】:

      也许你应该创建自己的用户主体模型类来确定它有正确的用户名,像这样。

      public class StompPrincipal implements Principal {
          String name;
          String userid;
        
          public StompPrincipal(String name) {
              this.name = name;
          }
          public String getUserid() {
              return userid;
          }
      
          public void setUserid(String userid) {
              this.userid = userid;
          }
          @Override
          public String getName() {
              return name;
          }
      }
      

      您还应该创建一个DefaultHandshakeHandler 实现以使用您选择的名称创建用户。

      public class CustomHandshakeHandler extends DefaultHandshakeHandler {
          // Custom class for storing principal
          @Override
          protected Principal determineUser(ServerHttpRequest request,
                                            WebSocketHandler wsHandler,
                                            Map<String, Object> attributes) {
      
              // Generate principal with UUID as name
              return new StompPrincipal(UUID.randomUUID().toString());
          }
      }
      

      最后将这个CustomHandshakeHandler 类添加到setHandshakeHandler 方法中,如下所示

       @Override
          public void registerStompEndpoints(StompEndpointRegistry registry) {
      
              registry.addEndpoint("/ws")
                      .setAllowedOrigins("*")
                      .setHandshakeHandler(new CustomHandshakeHandler());
          }
      

      【讨论】:

      • 干杯,如果是匿名用户,那确实很方便,但我正在使用 spring security 来完成所有登录工作,它工作正常。您还可以查看日志消息,发现用户“admin”的正确会话 id (d74f49b3-bb63-580f-b862-81647cc712b3) 与该用户的订阅请求匹配。
      • 我认为您可以在determineUser 函数中将StompPrincipal 的构造函数参数设置为SecurityContextHolder.getContext().getAuthentication().getName()。等待它是否工作。
      • 感谢您的评论,但正如我在之前的评论中指出的那样,我认为 Principal 类不是问题,只需查看会话 id,“admin”会向右解析“会话标识”。这里唯一的问题是我没有在客户端收到它。
      • Stomp 不知道 'admin' 用户。因此,如果您想向该用户发送消息,您需要知道它是 'sessionid'。stomp 知道它的方法之一是创建自定义主体如上面的代码。另一种方法是使用包括用户名信息在内的标头参数打开连接。之后,您可以将用户名和会话 ID 存储在列表中。并从列表中发送带有其会话 ID 的消息给管理员。
      • Stomp 是 websocket 之上的一个额外层,如果我理解正确,如果你做 spring security + stomp,主体是已知的。我做了一个快速测试,将“log.debug(SecurityContextHolder.getContext().getAuthentication().getName())”添加到带有@MessageMapping的方法中,它确实打印了登录用户。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-22
      • 2021-04-08
      • 1970-01-01
      • 1970-01-01
      • 2021-05-16
      相关资源
      最近更新 更多