ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • STOMP 메시지 전송 시 security context holder에서 유저 정보를 가져올 수 없는 error
    트러블슈팅 2024. 8. 16. 17:48

    문제

    Jul 24 17:13:51 ip-172-31-44-203 web[1195047]: ----------------- stomp command : SEND
    Jul 24 17:13:51 ip-172-31-44-203 web[1195047]: ----------------- authentication : kimtahwn@naver.com
    Jul 24 17:13:51 ip-172-31-44-203 web[1195047]: 2024-07-24T17:13:51.882+09:00 ERROR 1195047 --- [nboundChannel-6] .WebSocketAnnotationMethodMessageHandler : Unhandled exception from message handler method
    Jul 24 17:13:51 ip-172-31-44-203 web[1195047]: java.lang.NullPointerException: Cannot invoke "org.springframework.security.core.Authentication.getPrincipal()" because "authentication" is null
    Jul 24 17:13:51 ip-172-31-44-203 web[1195047]: #011at com.example.CatchStudy.service.UsersService.getEmail(UsersService.java:75) ~[!/:0.0.1-SNAPSHOT]
    Jul 24 17:13:51 ip-172-31-44-203 web[1195047]: #011at com.example.CatchStudy.service.UsersService.getCurrentUserId(UsersService.java:55) ~[!/:0.0.1-SNAPSHOT]
    

    Stomp를 사용하여 메시지를 전송할 때, channelInterCeptor에서 securityContextHolder에 사용자의 정보가 저장되는 것을 확인했지만 메시지를 저장하는 메소드가 호출될 때(Controller, Service)는 securityContextHolder에서 사용자의 정보를 가져오지 못 해 에러가 발생한다.


    원인

    SecurityContext의 전파: SecurityContextHolder는 스레드 로컬(Thread Local) 저장소를 사용하여 현재 스레드에 대한 보안 컨텍스트를 저장한다.

    send 명령을 처리하는 Interceptor 핸들러와 controller가 서로 다른 스레드에서 실행되어, SecurityContext가 전파되지 않아 null이 발생한다.

     

     

    • StompHandler (ChannelInterceptor):
      • StompHandler는 WebSocket 통신에서 채널을 가로채는 인터셉터 역할 -> 메시지 전송 전에 스레드 실행
      • 이 클래스의 preSend 메소드는 STOMP 메시지가 채널을 통해 전송되기 전에 실행이 단계에서 주로 보안 및 인증 관련 처리를 하며, 메시지의 헤더를 분석하고 인증 토큰을 검증하여 Spring Security Context에 사용자 정보를 설정
      • Spring에서는 WebSocket 메시지 처리를 위해 내부적으로 스레드 풀을 사용, preSend 메소드는 스레드 풀 내의 스레드에서 실행
    • MessageController (메시지 처리 컨트롤러):
      • MessageController 클래스의 메소드(createMessage)는 클라이언트가 메시지를 전송했을 때 실행.
      • 클라이언트가 /chatRoomId/chat로 메시지를 전송하면 이 메소드가 호출되어 메시지를 처리하고 응답을 생성
      • 메시지 컨트롤러 메소드는 WebSocket 메시지 처리 스레드 풀에서 실행되고 Spring은 각 메시지에 대해 적절한 스레드를 할당하여 메시지를 처리

     

    ** Spring에서 HTTP 와 WebSocket의 차이

    - HTTP :

      동기 방식이며 각 요청마다 thread 할당

     

    - WebSocket :

      비동기 방식이며 지속적 연결을 통해 실시간 통신을 지원

      핸드셰이크를 통해 연결이 설정되면 spring에서는 비동기적으로 메시지를 처리하기 위해 스레드를 사용하며 연결이 해제되면 스레드 해제


    해결

    • 메시지를 전송할 때, front에게 header에 jwtToken을 전달받아 해결
    @MessageMapping("/{chatRoomId}/chat")
    @SendTo("/sub/{chatRoomId}/chat")
    public void createMessage(@DestinationVariable long chatRoomId, MessageRequestDto messageRequestDto,
                              @Header("Authorization") String jwtToken) {
        ...
    }

    '트러블슈팅' 카테고리의 다른 글

    STOMP 연결, 해제 시 header 값 Error  (0) 2024.08.15
    WebSocket 연결 시, JWT Token Error  (0) 2024.08.15
    Redis protected mode Error  (0) 2024.08.15
    spring security oauth2.0 kakao error  (0) 2024.08.15
    Github Actions Build 에러  (0) 2024.03.15
Designed by Tistory.