[그림일기 서비스 보고 공부하기] OncePerRequestFilter와 AuthenticationEntryPoint

오젼·2024년 9월 8일
0

Spring security에서 Jwt 인증 과정에서 발생하는 예외처리를 담당할 JwtAuthenticationEntryPoint를 구현하려고 했다.

역시나 그림일기 서비스가 굉장히 깔끔하게 구현이 된 것 같아서 따라하려고 봤더니 일반적으로 구현하는 방식과 차이가 있는 것을 발견했다.

JwtAuthenticationEntryPoint는 보통 AuthenticationEntryPoint를 implements 해서 구현한다.

그런데 그림일기 서비스는 OncePerRequestFilter를 extends 해서 JwtAuthenticationEntryPoint를 구현했다.

왜 그런가 살펴봤더니 AuthenticationEntryPoint를 implements 하면 AuthenticationException에 대한 예외처리만 할 수 있기 때문이었다.

단순히 인증이 실패했다는 예외처리만 하는 게 아니라, 만료된 토큰인지, 유효하지 않은 토큰인지 커스텀 익셉션을 구현해 세부적으로 나누어 처리하려면 OncePerRequestFilter를 extends 하는 게 더 나았던 것이다.

하지만 고민이 생겼다. claude는 spring security에서 기본적으로 정의된 흐름을 따르는 것이 좋다고 답변을 해주기 때문이다.

인터넷에서 검색했을 때 나오는 글들을 봐도 AuthenticationEntryPoint를 implements 하는 경우가 대부분이기도 했다.

챗지피티한테도 물어보며 답변을 읽다보니 결국 이것도 선택의 문제인 것 같다.

--> 라고 생각했는데...
https://velog.io/@hellonayeon/spring-boot-jwt-expire-exception
이 글을 보니 세부적으로 예외처리를 하는 게 맞아보인다;; 적어도 만료된 토큰에 대한 예외처리만이라도.. 왜냐면 토큰이 만료된 경우에 refresh token 요청을 해야하기 때문에...
원래는 프론트에서 401인 경우 무조건 refresh token 요청을 보내보게 했었는데
error code를 가지고 만료된 토큰인 경우에만 refresh token 요청을 보내게 하는 게 맞아 보인다.
또 블로그를 보니 JwtExceptionFilter로 네이밍을 하는 게 나아보여 나도 JwtExceptionFilter로 클래스명을 지었다.

아래는 그래도 고민한 흔적들.....

AuthenticationEntryPoint를 implements 하는 게 나을까, OncePerRequestFilter를 extends 하는 게 나을까?

JwtAuthenticationEntryPoint를 구현할 때 AuthenticationEntryPointOncePerRequestFilter 중 어떤 것을 선택할지는 애플리케이션의 구조와 요구 사항에 따라 달라집니다. 각각의 선택에 따른 장단점이 있으니 이를 고려해 보면 좋을 것 같습니다.

AuthenticationEntryPoint를 사용하는 경우:

  • 주된 목적: 인증되지 않은 사용자에게 401 상태 코드를 반환하는 역할을 담당합니다.
  • 권장되는 사용 상황:
    • 인증이 실패했을 때만 특정 로직을 처리하고, 그 외에는 기본적으로 필터 체인에 신경 쓰지 않으려는 경우 적합합니다.
    • 단순히 인증 실패에 대한 처리만 필요하고, 별도의 로직이 요구되지 않는 상황.
  • 장점:
    • AuthenticationEntryPoint는 Spring Security에서 인증 실패에 특화된 인터페이스로, 간단하게 인증 실패 처리를 정의할 수 있습니다.
    • 인증 실패와 관련된 표준적인 로직을 구현하기 쉽습니다.
  • 단점:
    • 필터 체인 내에서 복잡한 예외 처리나 추가적인 로직을 처리하기는 어려울 수 있습니다.
    • 다른 예외(예: 권한 부족, 토큰 형식 오류 등)를 다루는 데 제한적일 수 있습니다.

OncePerRequestFilter를 사용하는 경우:

  • 주된 목적: 모든 요청에 대해 필터링 로직을 수행할 수 있으며, 예외 처리를 커스터마이징할 수 있습니다.
  • 권장되는 사용 상황:
    • 요청에 대한 더 많은 제어가 필요할 때, 특히 JWT 토큰의 유효성 검사, 예외 처리, 응답 커스터마이징 등을 모두 한 곳에서 처리해야 하는 경우.
    • 인증뿐만 아니라 다양한 종류의 예외 처리나 추가적인 필터링 로직이 필요할 때.
  • 장점:
    • 필터 체인 내에서 모든 요청에 대해 동작하기 때문에, 인증이 필요 없는 요청이나 특정 조건에서도 예외 처리를 적용할 수 있습니다.
    • 예외 발생 시 응답 형식을 커스터마이징할 수 있으며, 인증뿐만 아니라 토큰 검증 관련 다양한 로직을 추가할 수 있습니다.
  • 단점:
    • 인증 실패와 관계없는 모든 요청에 대해 필터를 수행하기 때문에, 불필요한 오버헤드가 발생할 수 있습니다.
    • 코드가 복잡해질 수 있으며, 인증에만 집중된 로직이 아니라 요청 흐름 전체에 대한 책임을 가져야 할 수 있습니다.

결론:

  • 단순히 인증 실패를 처리하고 싶다면: AuthenticationEntryPoint를 사용하는 것이 더 적절해 보입니다. 이 경우 Spring Security의 표준적인 흐름을 따르기 때문에 간결하고 명확하게 인증 실패를 처리할 수 있습니다.
  • 더 많은 제어와 예외 처리가 필요하다면: 현재처럼 OncePerRequestFilter를 사용하는 것이 맞습니다. 특히, JWT 토큰 검증, 다양한 에러 처리, 응답 형식 커스터마이징 등을 한 곳에서 처리할 수 있기 때문에 더 유연하게 동작합니다.

당신이 현재 보고 있는 코드의 구조는 다양한 인증 및 예외 상황을 처리하려는 복잡한 요구 사항에 맞춰져 있는 것으로 보이므로, OncePerRequestFilter가 적절한 선택으로 보입니다.

spring security의 인증 과정의 예외처리는 어떻게 돼있을까?

Spring Security에서 인증 예외 처리는 AuthenticationException을 기반으로 설계되어 있으며, 이것은 의도적인 설계입니다. Spring Security는 표준화된 인증 흐름을 통해 일관성 있게 예외를 처리하고, 보안을 유지하려는 목적을 가지고 있습니다. 하지만 이를 확장하거나 커스터마이징하는 것도 가능하며, 이 부분에서 선택이 중요해집니다.

1. Spring Security의 기본 인증 예외 처리

Spring Security는 기본적으로 인증 과정에서 발생하는 모든 예외를 AuthenticationException으로 처리합니다. 이 예외는 인증 과정에서 문제가 발생했을 때 발생하며, 다양한 인증 관련 예외(예: 비밀번호 오류, 토큰 오류 등)의 상위 클래스입니다.

인증 흐름과 예외 처리 과정:

  • 사용자가 보호된 리소스에 접근하려고 할 때, Spring Security는 AuthenticationManager를 통해 인증을 시도합니다.
  • 이 과정에서 인증에 실패하면 AuthenticationException이 발생합니다.
  • 기본적으로 이 예외는 AuthenticationEntryPoint에 의해 처리되어, 인증되지 않은 사용자의 경우 401(Unauthorized) 응답을 반환합니다.

2. Spring Security가 AuthenticationException만 처리하도록 설계된 이유

  • 일관성 유지: Spring Security는 다양한 인증 방법(기본 인증, 폼 기반 인증, JWT 등)을 지원하므로, AuthenticationException 하나로 모든 인증 관련 예외를 처리함으로써 코드의 일관성과 단순성을 유지합니다.
  • 보안 유지: 특정한 예외 정보를 클라이언트에게 제공하는 것은 보안상 위험할 수 있습니다. 예를 들어, "잘못된 비밀번호"와 같은 구체적인 메시지를 제공하는 것은 공격자가 시스템의 취약성을 탐지하는 데 도움이 될 수 있습니다. Spring Security는 이를 방지하기 위해 인증 실패 시 일반적인 401 응답을 반환하도록 설계되어 있습니다.
  • 표준화된 구조: AuthenticationException을 사용하면 모든 인증 예외를 하나의 구조로 처리할 수 있으며, 이를 통해 전반적인 인증 처리 로직을 단순화할 수 있습니다.

3. Spring Security의 예외 처리 커스터마이징

Spring Security가 기본적으로 AuthenticationException만 처리하도록 설계되었지만, 이 기본 동작을 커스터마이징하는 것도 가능합니다. 예를 들어, JWT를 사용하는 경우 토큰이 만료되거나 잘못된 토큰 형식을 제공하는 등의 다양한 예외 상황을 세부적으로 처리할 필요가 있을 수 있습니다.

왜 커스터마이징할 수 있는가?

  • 특정 인증 방식에 대한 맞춤 예외 처리: JWT 같은 경우, 다양한 토큰 관련 예외가 발생할 수 있습니다(토큰 만료, 서명 오류, 부적절한 토큰 등). Spring Security의 기본 AuthenticationException 구조는 이러한 세부 상황을 충분히 표현하지 못할 수 있습니다. 그래서 더 세부적인 예외를 다루기 위해서는 OncePerRequestFilter처럼 커스터마이징된 처리가 필요할 수 있습니다.

  • 세부적인 에러 메시지 제공: 때로는 API 클라이언트에게 더 구체적인 에러 메시지를 반환해야 할 수 있습니다. 예를 들어, "토큰 만료" 또는 "유효하지 않은 토큰" 같은 에러를 클라이언트에게 명확하게 전달해야 할 필요가 있는 경우, Spring Security의 기본 인증 예외 처리 흐름을 확장해야 합니다.

4. Spring Security 예외 처리에 따를지, 커스터마이징할지 고려할 점

  • 기본 동작을 따르는 것이 좋을 때:
    • 일반적인 인증 예외 처리(예: 로그인 실패, 인증되지 않은 접근)만 필요하고, 세부적인 토큰 처리나 커스텀 로직이 필요하지 않은 경우.
    • 일관된 보안 정책을 유지하고 싶을 때. 기본 흐름을 따르면 보안 취약점이 덜 발생할 가능성이 높습니다.
  • 커스터마이징이 필요한 경우:
    • JWT 같은 토큰 기반 인증에서 다양한 예외 상황을 다루고, 클라이언트에게 더 구체적인 정보를 전달해야 하는 경우.
    • 특정 상황에 맞는 추가 로직이나 처리 과정이 필요한 경우.

5. 결론적으로 어떻게 해야 할까?

  • Spring Security의 설계 의도를 존중해야 하는 이유: Spring Security의 기본 설계는 표준화된 보안 구조를 제공하며, 복잡한 예외 처리나 보안 관련 문제를 피할 수 있는 강력한 프레임워크입니다. 기본적인 인증 처리에서는 이를 따르는 것이 유지보수성이나 보안 측면에서 좋습니다.
  • JWT 등 복잡한 예외 상황이 있을 경우: 커스터마이징은 특정한 필요에 따라 정당화될 수 있습니다. 현재의 구조에서 OncePerRequestFilter를 사용하여 JWT 토큰의 다양한 예외를 처리하는 것은 타당해 보입니다. 다만, 이 경우에도 필요 이상으로 세부적인 정보를 노출하지 않도록 주의해야 합니다.

즉, Spring Security의 표준 설계를 따르는 것은 일반적으로 권장되지만, 현재의 JWT와 관련된 복잡한 요구 사항에서는 커스터마이징이 필요한 경우가 많습니다.

0개의 댓글