从Spring websocket stomp服务器断开客户端会话

时间:2015-02-16 23:17:08

标签: spring spring-boot stomp spring-websocket

我搜索了很多并且无法找到这个:有没有办法让一个spring websocket stomp服务器可以根据sessionId断开客户端(或者真的基于任何东西)?

在我看来,一旦客户端连接到服务器,就没有任何东西允许服务器断开客户端。

7 个答案:

答案 0 :(得分:13)

实际上使用一些变通办法可以达到你想要的效果。 为此你应该这样做:

  1. 使用java配置(不确定是否可以使用XML配置)
  2. WebSocketMessageBrokerConfigurationSupport 扩展您的配置类并实现 WebSocketMessageBrokerConfigurer 接口
  3. 创建自定义子协议websocket处理程序并从 SubProtocolWebSocketHandler
  4. 扩展它
  5. 在您的自定义子协议中,websocket处理程序覆盖 afterConnectionEstablished 方法,您将可以访问WebSocketSession:)
  6. 我已经创建了示例spring-boot项目,以展示我们如何从服务器端断开客户端会话: https://github.com/isaranchuk/spring-websocket-disconnect

答案 1 :(得分:4)

您还可以通过实施自定义WebSocketHandlerDecorator

来断开会话
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig<S extends ExpiringSession> extends AbstractSessionWebSocketMessageBrokerConfigurer<S> {

    @Override
    public void configureWebSocketTransport(final WebSocketTransportRegistration registration) {
        registration.addDecoratorFactory(new WebSocketHandlerDecoratorFactory() {
            @Override
            public WebSocketHandler decorate(final WebSocketHandler handler) {
                return new WebSocketHandlerDecorator(handler) {
                    @Override
                    public void afterConnectionEstablished(final WebSocketSession session) throws Exception {

                        session.close(CloseStatus.NOT_ACCEPTABLE);
                        super.afterConnectionEstablished(session);
                    }
                };
            }
        });
        super.configureWebSocketTransport(registration);
    }


    @Override
    protected void configureStompEndpoints(final StompEndpointRegistry registry) {
    registry.addEndpoint("/home")
            .setHandshakeHandler(new DefaultHandshakeHandler(
                    new UndertowRequestUpgradeStrategy() // If you use undertow
                    // new JettyRequestUpgradeStrategy()
                    // new TomcatRequestUpgradeStrategy()
            ))
            .withSockJS();
    }
}

答案 2 :(得分:1)

据我所知,API没有提供您要查找的内容,在服务器端您只能检测断开连接事件。如果你想断开某个客户端,我认为你必须采取一些小问题,例如这一个:

  1. 编写一个能够触发断开连接的客户端javascript函数
  2. 只要客户端连接到服务器,就在javascript中生成客户端ID并将其发送到服务器。记住客户端上的ID,您将在步骤(4)中需要它。
  3. 当您希望服务器断开与特定客户端的连接(由ID标识)时,请将包含该ID的消息发送回客户端。
  4. 现在,您的客户端javascript评估从服务器发送的消息,并决定调用您在步骤(1)中编写的断开连接函数。
  5. 您的客户自行断开连接。
  6. 解决方法有点麻烦,但它可以正常工作。

答案 3 :(得分:1)

首先,您必须通过继承将一个类作为User类引入,然后像这样使用它:

if (userObject instanceof User) {
    User user = (User) userObject;
    if (user.getId().equals(userDTO.getId())) {
       for (SessionInformation information : sessionRegistry.getAllSessions(user, true)) {
          information.expireNow();
       }
    }
}    

答案 4 :(得分:1)

我依靠@DánielKis的想法,并实现了websocket会话管理,其关键点是在类似Singleton的对象中存储供经过身份验证的用户使用的websocket会话。

// WebSocketConfig.java

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
        registration.addDecoratorFactory(new WebSocketHandlerDecoratorFactory() {
            @Override
            public WebSocketHandler decorate(final WebSocketHandler handler) {
                return new WebSocketHandlerDecorator(handler) {

                    @Override
                    public void afterConnectionEstablished(final WebSocketSession session) throws Exception {

                        // We will store current user's session into WebsocketSessionHolder after connection is established
                        String username = session.getPrincipal().getName();
                        WebsocketSessionHolder.addSession(username, session);

                        super.afterConnectionEstablished(session);
                    }
                };
            }
        });
    }
}

用于存储Websocket用户会话的类 WebsocketSessionHolder 。我使用“同步”块来确保线程安全。实际上,此块并不是昂贵的操作,因为不经常使用每种方法(addSession和closeSessions)(在建立和终止连接时)。这里不需要使用ConcurrentHashMap或SynchronizedMap,因为我们在这些方法中对列表执行了很多操作。

// WebsocketSessionHolder.java

public class WebsocketSessionHolder {

    static {
        sessions = new HashMap<>();
    }
    
    // key - username, value - List of user's sessions
    private static Map<String, List<WebSocketSession>> sessions;

    public static void addSession(String username, WebSocketSession session)
    {
        synchronized (sessions) {
            var userSessions = sessions.get(username);
            if (userSessions == null)
                userSessions = new ArrayList<WebSocketSession>();

            userSessions.add(session);
            sessions.put(username, userSessions);
        }
    }

    public static void closeSessions(String username) throws IOException 
    {
        synchronized (sessions) {
            var userSessions = sessions.get(username);
            if (userSessions != null)
            {
                for(var session : userSessions) {
                    // I use POLICY_VIOLATION to indicate reason of disconnecting for a client
                    session.close(CloseStatus.POLICY_VIOLATION);
                }
                sessions.remove(username);
            }
        }
    }
}

最后一步-终止(断开连接)指定的用户Websocket会话(在示例中为“ ADMIN”),例如在某些控制器中

//PageController.java

@Controller
public class PageController {
    @GetMapping("/kill-sessions")
    public void killSessions() throws Exception {

        WebsocketSessionHolder.closeSessions("ADMIN");
    }
}

答案 5 :(得分:0)

如果是xml配置,您可以在<websocket:decorator-factories>的{​​{1}}中使用<websocket:transport>。 创建实现<websocket:message-broker>方法的自定义WebSocketHandlerDecoratorWebSocketHandlerDecoratorFactory

答案 6 :(得分:0)

这似乎很简短,但是我不确定您的情况下实现的样子。但是,我认为在某些情况下需要采取这种解决方法/解决方案:

  1. 在后端设置超时(例如30秒):
    • 这是使用Spring Boot Websocket(和Tomcat)的方法:
    @Bean
    public ServletServerContainerFactoryBean websocketContainer() {
        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
        container.setMaxSessionIdleTimeout(MAX_SESSION_IDLE_TIMEOUT); 
        return container;
    }
  1. 如果要保持会话打开-继续发送消息或积极发送ping / pongs。如果您要断开会话连接,请在您的应用程序中合适的位置停止ping / pong交互。

当然,如果您想立即断开连接,这似乎不是一个合适的解决方案。但是,如果您只是想减少活动连接的数量,则ping / pong可能是一个不错的选择,因为它仅在主动发送消息时才保持会话​​打开,从而防止会话过早关闭。

相关问题