Spring Boot

WebSocket jwt 인증 처리 (feat. filter vs interceptor)

최-코드 2024. 8. 21. 23:34

상황 : afterConnectionEstablished 메소드 내에서 Authentication 객체를 통해 userId를 가져오려고 했지만 null 예외가 발생했다.

 

원인 : websocket 통신은 기본적으로 security filter chain을 거치지 않는다. 또한 afterConnectionEstablished은 handshake 과정이 끝난(응답까지 끝난 상태) 이후이므로 Authentication이 살아있을 수가 없다.

필터는 dispatcher servlet 전이고 interceptor는 controller과 dispatcher servelt 사이에 있다.
cf) dispatcher servlet는 http 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 컨트롤러에 위임해주는 프론트 컨트롤러이다.

 

해결법 : handshake는 http 통신이므로 security filter chain을 거치게 된다. 또한 사전에 정의된 HandshakeInterceptor를 통해 handshake가 이뤄질 때 서버에 저장된 Authentication을 통해 userId를 attributes에 넣어주면 된다.

 

@Component
@RequiredArgsConstructor
@Slf4j
public class CustomHandshakeInterceptor implements HandshakeInterceptor {
    private final JWTUtil jwtUtil;

    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
                                   WebSocketHandler wsHandler, Map<String, Object> attributes) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal();
        attributes.put("userId", customUserDetails.getUserId());
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
                               WebSocketHandler wsHandler, Exception exception) {
    }
}

 

위와 같이 보통 beforeHandshake 메소드 안에 인증 과정이나, websocketsession에 넣어줄 정보를 attributes에 넣어준다.