JWT 인증 기반 WebSocket 연결 실패, 원인과 해결법

2025. 6. 5. 10:28·TIL (Today I Learned)

❗ 문제점

채팅 시스템에서 메시지를 전송하려 했으나 메시지 전송이 이뤄지지 않는 문제가 발생했습니다.

 

테스트용 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
'TIL (Today I Learned)' 카테고리의 다른 글
  • JVM의 개념과 내부 구조 정리
  • Synchronous 🆚 Asynchronous | RabbitMQ 기반 비동기 처리 구조
  • Spring Boot 설정값 바인딩 정리: @Value vs @ConfigurationProperties
  • 동시성 제어는 어떻게 할까❓
기만나🐸
기만나🐸
공부한 내용을 기록합시다 🔥🔥🔥
  • 기만나🐸
    기만나의 공부 기록 🤓
    기만나🐸
  • 전체
    오늘
    어제
    • ALL (147)
      • TIL (Today I Learned) (56)
      • Dev Projects (15)
      • Algorithm Solving (67)
        • Java (52)
        • SQL (15)
      • Certifications (8)
        • 정보처리기사 실기 (8)
  • 인기 글

  • 태그

    jQuery
    mysql
    GROUP BY
    자료구조
    Google Fonts
    Firebase
    jwt
    greedy
    HTML
    DFS
    다이나믹프로그래밍
    sql
    join
    완전탐색
    bootstrap
    programmers
    BFS
    dp
    백트래킹
    javascript
    시뮬레이션
    jpa
    그리디
    CSS
    websocket
    Subquery
    백준
    BOJ
    프로그래머스
    java
  • 최근 글

  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.3
기만나🐸
JWT 인증 기반 WebSocket 연결 실패, 원인과 해결법
상단으로

티스토리툴바