在没有弹簧认证的情况下通过Web套接字多次回复

Guy*_*nat 15 java spring spring-websocket

注意:请参阅问题底部的更新,了解我最终得出的结论.

我需要通过发送请求消息的Web套接字向请求发送多个响应,第一个快速响应,其他响应在数据验证之后(10到60秒之后,从多个并行线程).

我无法让后来的响应停止在所有打开的网络套接字上播放.如何让它们只发送到初始Web套接字?或者我应该使用Spring STOMP之外的东西(因为,​​老实说,我想要的是路由到各种功能的消息,我不需要或想要能够广播到其他网络套接字,所以我怀疑我可以写信息经销商自己,即使它正在重新发明轮子).

我没有使用Spring身份验证(这是在改进遗留代码中).

在初始返回消息中,我可以使用@SendToUser,即使我们没有用户,Spring也只将返回值发送到发送消息的websocket.(见这个问题).

但是,响应速度较慢,我认为我需要使用SimpMessagingTemplate.convertAndSendToUser(用户,目的地,消息),但我不能,因为我必须传入用户,而我无法弄清楚用户是什么用户使用SendToUser.我试图按照这个问题中的步骤进行操作,但是在未经过身份验证时没有使它工作(在这种情况下,principal.getName()返回null).

我已经为原型大大简化了这一点,所以不要担心同步线程或任何东西.我只是希望网络套接​​字正常工作.

这是我的控制器:

@Controller
public class TestSocketController
{
  private SimpMessagingTemplate template;

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

  // This doesn't work because I need to pass something for the first parameter.
  // If I just use convertAndSend, it broacasts the response to all browsers
  void setResults(String ret)
  {
    template.convertAndSendToUser("", "/user/topic/testwsresponse", ret);
  }

  // this only sends "Testing Return" to the browser tab hooked to this websocket
  @MessageMapping(value="/testws")
  @SendToUser("/topic/testwsresponse")
  public String handleTestWS(String msg) throws InterruptedException
  {
    (new Thread(new Later(this))).start();
    return "Testing Return";
  }

  public class Later implements Runnable
  {
    TestSocketController Controller;
    public Later(TestSocketController controller)
    {
        Controller = controller;
    }

    public void run()
    {
        try
        {
            java.lang.Thread.sleep(2000);

            Controller.setResults("Testing Later Return");
        }
        catch (Exception e)
        {
        }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

对于记录,这是浏览器端:

var client = null;
function sendMessage()
{
    client.send('/app/testws', {}, 'Test');
}

// hooked to a button
function test()
{
    if (client != null)
    {
        sendMessage();
        return;
    }

    var socket = new SockJS('/application-name/sendws/');
    client = Stomp.over(socket);
    client.connect({}, function(frame)
    {
        client.subscribe('/user/topic/testwsresponse', function(message)
        {
            alert(message);
        });

        sendMessage();
    });
});
Run Code Online (Sandbox Code Playgroud)

这是配置:

@Configuration
@EnableWebSocketMessageBroker
public class TestSocketConfig extends AbstractWebSocketMessageBrokerConfigurer
{
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config)
    {
        config.setApplicationDestinationPrefixes("/app");
        config.enableSimpleBroker("/queue", "/topic");
        config.setUserDestinationPrefix("/user");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry)
    {
        registry.addEndpoint("/sendws").withSockJS();
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:由于安全问题涉及通过其他websockets发送信息的可能性而不是原始套接字,我最后向我的小组推荐我们不使用STOMP的Spring 4.0实现而不是Web套接字.我理解为什么Spring团队以他们这样做的方式做到了,而且它比我们需要的更强大,但我们项目的安全限制足够严重,实际要求很简单,我们决定采用不同的方式.这并不会使下面的答案无效,因此请根据您的项目需求做出自己的决定.至少我们希望所有人都了解技术的局限性,无论好坏.

med*_*088 6

为什么不为每个客户使用单独的主题?

  1. 客户端生成会话ID.

    var sessionId = Math.random().toString(36).substring(7);

  2. 客户订阅/ topic/testwsresponse/{sessionId},然后向'/ app/testws/{sessionId}'发送消息.

  3. 在您的控制器中,您使用@MessageMapping(value ="/ testws/{sessionId}")并删除@SendToUser.您可以使用@DestinationVariable访问方法中的sessionId.
  4. 控制器向/ topic/testwsresponse/{sessionId}发送进一步的响应.

当您使用用户目标时,本质上Spring在内部执行类似的操作.由于您不使用Spring身份验证,因此您不能依赖此机制,但您可以轻松实现自己的身份,如上所述.

var client = null;
var sessionId = Math.random().toString(36).substring(7);
function sendMessage()
{
    client.send('/app/testws/' + sessionId, {}, 'Test');
}

// hooked to a button
function test()
{
    if (client != null)
    {
        sendMessage();
        return;
    }

    var socket = new SockJS('/application-name/sendws/');
    client = Stomp.over(socket);
    client.connect({}, function(frame)
    {
        client.subscribe('/topic/testwsresponse/' + sessionId, function(message)
        {
            alert(message);
        });

        // Need to wait until subscription is complete
        setTimeout(sendMessage, 1000);
    });
}); 
Run Code Online (Sandbox Code Playgroud)

控制器:

@Controller
public class TestSocketController
{
    private SimpMessagingTemplate template;

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

    void setResults(String ret, String sessionId)
    {
        template.convertAndSend("/topic/testwsresponse/" + sessionId, ret);
    }

    @MessageMapping(value="/testws/{sessionId}")
    public void handleTestWS(@DestinationVariable String sessionId, @Payload String msg) throws InterruptedException
    {
        (new Thread(new Later(this, sessionId))).start();
        setResults("Testing Return", sessionId);
    }

    public class Later implements Runnable
    {
        TestSocketController Controller;
        String sessionId;
        public Later(TestSocketController controller, String sessionId)
        {
            Controller = controller;
            this.sessionId = sessionId;
        }

        public void run()
        {
            try
            {
                java.lang.Thread.sleep(2000);

                Controller.setResults("Testing Later Return", sessionId);
            }
            catch (Exception e)
            {
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

刚刚测试过,按预期工作.