❗ 문제점
채팅 시스템에서 메시지를 전송하려 했으나 메시지 전송이 이뤄지지 않는 문제가 발생했습니다.
테스트용 chat.html 페이지에서는 JWT AccessToken을 입력받아 STOMP connect 헤더에만 포함시켰지만, 이 값은 WebSocket 핸드셰이크 단계에선 전달되지 않아 인증이 누락되었습니다.
클라이언트와 서버 양쪽에서 다음과 같은 에러가 확인됩니다.
클라이언트


서버
2025-04-23T15:44:45.565+09:00 WARN 8236 --- [table-now] [nio-8080-exec-7] o.s.w.s.s.t.h.DefaultSockJsService : Origin check enabled but transport 'jsonp' does not support it.
2025-04-23T15:45:14.784+09:00 INFO 8236 --- [table-now] [MessageBroker-2] o.s.w.s.c.WebSocketMessageBrokerStats : WebSocketSession[0 current WS(0)-HttpStream(0)-HttpPoll(0), 0 total, 0 closed abnormally (0 connect failure, 0 send limit, 0 transport error)], stompSubProtocol[processed CONNECT(0)-CONNECTED(0)-DISCONNECT(0)], stompBrokerRelay[null], inboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], outboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], sockJsScheduler[pool size = 4, active threads = 1, queued tasks = 2, completed tasks = 1]
- STOMP 연결 관련 프레임 (CONNECT, CONNECTED, DISCONNECT)이 전혀 수신되지 않음
- 현재 WebSocket 세션이 존재하지 않음
🔍 원인
1. WebSocket 연결 시 STOMP CONNECT 프레임이 서버에 도달하지 않음
서버 로그에서 CONNECT, CONNECTED, DISCONNECT 프레임 모두 감지되지 않음
→ WebSocket 핸드셰이크 단계에서 인증 실패 등으로 연결이 끊긴 상태임을 의미.
2. JWT 인증 실패의 원인: Authorization 헤더 전달 누락
서버의 `JwtHandshakeInterceptor`에서는 다음 코드로 인증 토큰을 추출하려고 함:
String accessToken = httpRequest.getHeader(HttpHeaders.AUTHORIZATION);
그러나 브라우저에서 WebSocket을 연결할 때(`new WebSocket(...)`)는 커스텀 헤더 전송이 불가능하며, `Authorization`도 포함되지 않음.
즉, 브라우저에서 Authorization 헤더가 아예 전송되지 않음.
따라서 `getHeader()`는 항상 null을 반환하게 됨.
3. STOMP `connect()`의 헤더는 Handshake에 무관
STOMP 클라이언트의 `stompClient.connect(headers, callback)`에 `Authorization` 헤더를 설정하더라도,
이 헤더는 WebSocket 연결 이후 STOMP 프로토콜 상의 프레임에 포함되는 것이지,
초기 WebSocket 핸드셰이크 요청에는 포함되지 않음.
WebSocket 핸드셰이크 시점에는 해당 헤더가 포함되지 않으므로, `HandshakeInterceptor`에서는 접근 불가.
🛠️ 해결 방법
WebSocket handshake 시 토큰을 쿼리 파라미터로 전달하도록 수정:
JavaScript
const socket = new WebSocket("/ws/chat?token=" + encodeURIComponent(token));
JwtHandshakeInterceptor
String accessToken = httpRequest.getParameter("token");
📌 결론 및 참고 사항
- HTML 테스트 페이지(chat.html)는 <script> 기반으로 WebSocket 연결을 시도하며, 이때 브라우저 보안 정책(CORS + WebSocket API 제한)으로 인해 커스텀 헤더(Authorization 포함) 전송이 불가능합니다.
- STOMP의 connect()에 설정한 헤더는 `@MessageMapping` 대상 컨트롤러에서만 유효하며, WebSocket Handshake에는 반영되지 않습니다.
- 따라서 테스트 환경에서는 쿼리 파라미터로 토큰을 전달하고, `HttpServletRequest.getParameter()`를 통해 추출하여 인증 처리를 우회해야 합니다.
⚠️ 주의:
운영 환경에서는 쿼리 파라미터로 토큰을 전달하는 방식은 보안상 매우 취약하므로 사용 금지입니다.
→ Spring Security와 WebSocketSecurity 구성을 통해 Authorization 헤더 인증 처리 방식 적용이 필요
'TIL (Today I Learned)' 카테고리의 다른 글
| JVM의 개념과 내부 구조 정리 (4) | 2025.06.19 |
|---|---|
| Synchronous 🆚 Asynchronous | RabbitMQ 기반 비동기 처리 구조 (2) | 2025.06.05 |
| Spring Boot 설정값 바인딩 정리: @Value vs @ConfigurationProperties (2) | 2025.05.24 |
| 동시성 제어는 어떻게 할까❓ (0) | 2025.03.26 |
| Windows 개발 환경에서 AWS 활용 과제 | Spring Boot v3.3.3 & Gradle 8.12 (0) | 2025.03.21 |