Spring websocket stomp 서버에서 클라이언트 세션 연결 해제
꽤 많이 검색했지만 찾을 수 없었습니다.sessionId(또는 실제로 모든 것을 기반으로)에 따라 스프링 웹 소켓 stomp 서버가 클라이언트를 절단할 수 있는 방법이 있습니까?
클라이언트가 서버에 접속하면 서버가 클라이언트의 접속을 끊을 수 있는 것은 아무것도 없는 것 같습니다.
실제로 몇 가지 회피책을 사용하면 원하는 것을 달성할 수 있습니다.그러기 위해서는 다음 작업을 수행해야 합니다.
- Java 구성 사용(XML 구성 사용 가능 여부 확인 안 함)
- Web Socket Message Broker Configuration Support에서 구성 클래스를 확장하고 Web Socket Message Broker Configr 인터페이스를 구현합니다.
- 사용자 지정 하위 프로토콜 웹 소켓 핸들러를 생성하여 SubProtocolWebSocketHandler 클래스에서 확장
- 커스텀 서브프로토콜 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가 원하는 기능을 제공하지 않는 것으로 알고 있는 한 서버 측에서는 연결 해제 이벤트만 탐지할 수 있습니다.특정 클라이언트의 접속을 끊는 경우는, 다음과 같은 간단한 회피책을 강구할 필요가 있다고 생각합니다.
- 절단을 트리거할 수 있는 클라이언트 측 Javascript 함수를 작성합니다.
- 클라이언트가 서버에 접속되는 즉시 Javascript에서 클라이언트 ID를 생성하여 서버로 전송합니다.클라이언트의 ID를 기억하십시오. (4) 단계에서 ID가 필요합니다.
- 서버가 특정 클라이언트(ID로 식별됨)와의 접속을 끊을 때 ID가 포함된 메시지를 클라이언트에 다시 보냅니다.
- 이제 클라이언트 javascript가 서버에서 보낸 메시지를 평가하여 스텝 (1)에서 작성한 연결 해제 함수를 호출하기로 결정합니다.
- 클라이언트의 접속이 끊어집니다.
해결 방법이 좀 번거롭지만 효과가 있을 것입니다.
저는 @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
방법.
이것은 간단한 것처럼 보일지 모르지만, 당신의 경우 구현이 어떻게 보일지 확신할 수 없습니다.단, 이 회피책/솔루션이 필요한 상황이 몇 가지 있다고 생각합니다.
- 백엔드의 타임아웃을 설정합니다(예를 들어 30초).
- Spring Boot Websocket(및 Tomcat)에서는 다음과 같이 실행할 수 있습니다.
@Bean
public ServletServerContainerFactoryBean websocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxSessionIdleTimeout(MAX_SESSION_IDLE_TIMEOUT);
return container;
}
- 세션을 열린 상태로 유지하려면 메시지를 계속 보내거나 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
'source' 카테고리의 다른 글
대규모 비즈니스 애플리케이션을 위한 대응 아키텍처 (0) | 2023.03.27 |
---|---|
jQuery AJAX 및 JSON 형식 (0) | 2023.03.27 |
플러그인을 설치하기 위한 FTP 자격 증명을 요청하는 WordPress (0) | 2023.03.27 |
스프링 부트 - 비관리형 (0) | 2023.03.27 |
NSManagedObject 속성 값에 대한 NSNull 처리 (0) | 2023.03.27 |