source

Spring websocket stomp 서버에서 클라이언트 세션 연결 해제

goodcode 2023. 3. 27. 23:16
반응형

Spring websocket stomp 서버에서 클라이언트 세션 연결 해제

꽤 많이 검색했지만 찾을 수 없었습니다.sessionId(또는 실제로 모든 것을 기반으로)에 따라 스프링 웹 소켓 stomp 서버가 클라이언트를 절단할 수 있는 방법이 있습니까?

클라이언트가 서버에 접속하면 서버가 클라이언트의 접속을 끊을 수 있는 것은 아무것도 없는 것 같습니다.

실제로 몇 가지 회피책을 사용하면 원하는 것을 달성할 수 있습니다.그러기 위해서는 다음 작업을 수행해야 합니다.

  1. Java 구성 사용(XML 구성 사용 가능 여부 확인 안 함)
  2. Web Socket Message Broker Configuration Support에서 구성 클래스를 확장하고 Web Socket Message Broker Configr 인터페이스를 구현합니다.
  3. 사용자 지정 하위 프로토콜 웹 소켓 핸들러를 생성하여 SubProtocolWebSocketHandler 클래스에서 확장
  4. 커스텀 서브프로토콜 Web 소켓핸들러로 afterConnectionEstablished 메서드가 덮어쓰면 WebSocketSession에 액세스 할 수 있습니다.

서버측에서 클라이언트세션을 절단하는 방법을 나타내는 샘플 스프링 부트 프로젝트를 작성했습니다.https://github.com/isaranchuk/spring-websocket-disconnect

, 으로, 도 있습니다.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();
    }
}

API가 원하는 기능을 제공하지 않는 것으로 알고 있는 한 서버 측에서는 연결 해제 이벤트만 탐지할 수 있습니다.특정 클라이언트의 접속을 끊는 경우는, 다음과 같은 간단한 회피책을 강구할 필요가 있다고 생각합니다.

  1. 절단을 트리거할 수 있는 클라이언트 측 Javascript 함수를 작성합니다.
  2. 클라이언트가 서버에 접속되는 즉시 Javascript에서 클라이언트 ID를 생성하여 서버로 전송합니다.클라이언트의 ID를 기억하십시오. (4) 단계에서 ID가 필요합니다.
  3. 서버가 특정 클라이언트(ID로 식별됨)와의 접속을 끊을 때 ID가 포함된 메시지를 클라이언트에 다시 보냅니다.
  4. 이제 클라이언트 javascript가 서버에서 보낸 메시지를 평가하여 스텝 (1)에서 작성한 연결 해제 함수를 호출하기로 결정합니다.
  5. 클라이언트의 접속이 끊어집니다.

해결 방법이 좀 번거롭지만 효과가 있을 것입니다.

저는 @Daniel Kis의 아이디어에 의존하여 인증된 사용자를 위한 웹 소켓 세션을 Singleton과 같은 오브젝트에 저장하는 키포인트로 웹 소켓 세션 관리를 구현했습니다.

// 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);
                    }
                };
            }
        });
    }
}

웹 소켓 사용자 세션을 저장하는 클래스 Web 소켓 세션홀더입니다. 저는 나사산 안전을 위해 '동기식' 블록을 사용합니다.각 메서드(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);
            }
        }
    }
}

또한 최종 터치 종료(접속 해제)된 지정된 사용자 웹 소켓 세션(예에서는 ADMIN)은 일부 컨트롤러에서 볼 수 있습니다.

//PageController.java

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

        WebsocketSessionHolder.closeSessions("ADMIN");
    }
}

xml 구성의 경우 다음 명령을 사용할 수 있습니다.<websocket:decorator-factories>에서<websocket:transport>고객님의<websocket:message-broker>커스텀 작성WebSocketHandlerDecorator그리고.WebSocketHandlerDecoratorFactory어떤 것이 실장되어 있는가?decorate방법.

이것은 간단한 것처럼 보일지 모르지만, 당신의 경우 구현이 어떻게 보일지 확신할 수 없습니다.단, 이 회피책/솔루션이 필요한 상황이 몇 가지 있다고 생각합니다.

  1. 백엔드의 타임아웃을 설정합니다(예를 들어 30초).
    • Spring Boot Websocket(및 Tomcat)에서는 다음과 같이 실행할 수 있습니다.
    @Bean
    public ServletServerContainerFactoryBean websocketContainer() {
        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
        container.setMaxSessionIdleTimeout(MAX_SESSION_IDLE_TIMEOUT); 
        return container;
    }
  1. 세션을 열린 상태로 유지하려면 메시지를 계속 보내거나 ping/pong을 능동적으로 보내십시오.세션을 절단할 경우 어플리케이션 내에서 적절한 장소에서 ping/pong 상호작용을 중지합니다.

물론 즉시 연결을 끊으려면 이 방법이 적절하지 않은 것 같습니다.그러나 단순히 활성 연결 수를 줄이려는 경우 ping/pong은 메시지가 활성화 전송되는 동안에만 세션을 열어두기 때문에 세션이 조기에 닫히지 않기 때문에 적합합니다.

먼저 상속으로 클래스를 사용자 클래스로 도입한 후 다음과 같이 사용해야 합니다.

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

언급URL : https://stackoverflow.com/questions/28552033/disconnect-client-session-from-spring-websocket-stomp-server

반응형