Projects/[Final] Shopping Mall Project

Spring Security 커스텀 필터 순서 오류로 통합 테스트 실패

montmer27 2026. 5. 5. 17:17

[Github Repo]

https://github.com/all-in-market/alarm-server/pull/1/changes/572197e6b04a29225972157c42681f9430483f1e

 

Feat/restock notification by ginsengcandy · Pull Request #1 · all-in-market/alarm-server

개요 재입고 알림 구현 작업 내용 고객 서버에서 재입고 이벤트 발생 시 알림 서버 REST 호출(엔드포인트: '{알림서버 주소}/internal/notifications/restock') 로컬에서는 localhost:8083 알림 서버는 호출 시

github.com

 

 

[빠른 요약]

JWT 인증 필터와 내부 인증 필터를 Spring Security filter chain에 등록하는 과정에서, 커스텀 필터를 기준으로 addFilterBefore()를 호출해 registered order 예외가 발생했다. 기준 필터를 Spring 기본 필터 또는 먼저 등록된 필터로 변경해 해결했다.

[상황]
JWT 기반 인증을 사용하는 서비스에서 JwtAuthenticationFilter와 내부 API 인증용 InternalAuthFilter를 함께 사용하고 있었다.
SecurityFilterChain 설정에서 /internal/** 경로는 내부 클라이언트 권한으로 보호하고, 일반 사용자 요청은 JWT 토큰으로 인증하도록 분리했다.

구현 의도는 다음과 같았다.

  1. 내부 API 요청이면 InternalAuthFilter가 먼저 인증 처리
  2. 일반 사용자 요청이면 JwtAuthenticationFilter가 JWT 검증 수행
  3. 이후 Spring Security 기본 인증 흐름 진행

이를 위해 InternalAuthFilter를 JwtAuthenticationFilter 앞에 두고 싶어 다음과 같이 설정했다.

.addFilterBefore(internalAuthFilter, JwtAuthenticationFilter.class)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)

로컬 개발 중에는 큰 문제가 없어 보였지만, 통합 테스트 실행 시 security context 초기화 단계에서 filter chain 생성이 실패했다.

[문제]
다음 두 테스트가 실패했다.

  • "잘못된 서명은 401을 반환한다" 테스트 실패
  • "만료된 타임스탬프(10분 전) 요청은 401을 반환한다" 테스트 실패

에러 로그:

Caused by: java.lang.IllegalArgumentException:
The Filter class
com.example.allinmarket.common.security.JwtAuthenticationFilter
does not have a registered order

원인은 addFilterBefore()의 두 번째 인자로 Spring Security가 순서를 알고 있는 필터 또는 이미 등록된 필터만 사용할 수 있다는 점이었다.

하지만 JwtAuthenticationFilter는 Spring 기본 필터가 아니라 커스텀 필터였고, 아직 order가 등록되지 않은 상태에서 기준 필터로 사용되었다.

즉, 아래 코드가 문제였다.

.addFilterBefore(internalAuthFilter, JwtAuthenticationFilter.class)

Spring 입장에서는
“JwtAuthenticationFilter가 filter chain 어디에 위치하는지 모르는데, 그 앞에 넣으라고?”
라는 상태라 예외를 던진 것이다.

해결 방법은 기준 필터를 Spring 기본 필터 또는 먼저 등록된 필터로 변경하는 것이었다.

수정 후:

.addFilterBefore(
    internalAuthFilter,
    UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(
    jwtAuthenticationFilter,
    InternalAuthFilter.class);

이렇게 변경해 명시적으로 순서를 지정했다.

최종 순서:

InternalAuthFilter
→ JwtAuthenticationFilter
→ UsernamePasswordAuthenticationFilter

수정 후 두 테스트가 통과했고, 잘못된 서명/만료 토큰에 대해 정상적으로 401 응답을 반환했다.