Spring Security 필터 체인 문제 해결 과정과 서버 동작 흐름 이해하기

coldrice99·2024년 11월 3일
0
post-thumbnail

문제 상황

회원가입 기능을 구현하고 /api/auth/signup 경로로 API 요청을 테스트하던 중, HTTP 200 OK 응답만 반환되고, 요청이 컨트롤러에 도달하지 않는 문제가 발생했습니다. 잘못된 URL로 요청해도 동일하게 200 OK 응답이 반환되어, 필터 체인이 요청을 제대로 전달하지 않는 것 같았습니다. 이 문제를 해결하는 과정에서 서버 시작 후 동작하는 순서와 필터 체인의 흐름에 대해 깊이 이해하게 되었습니다.


문제 해결을 위한 과정과 시행착오

1. Spring Security 설정 검토
  • 우선 SecurityFilterChain 설정을 재확인하며 인증이 필요한 요청과 허용된 요청을 제대로 구분하는지 점검했습니다.
  • /api/auth/** 경로에 대해서만 permitAll()로 설정하여 인증 없이 접근 가능하도록 했으며, 그 외 모든 요청에 대해 authenticated()로 설정하여 인증을 요구하도록 했습니다.
  • 또한, CSRF 비활성화와 세션 관리 정책을 STATELESS로 설정했으나 여전히 모든 요청에서 200 OK 응답만 반환되는 상태였습니다.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    // csrf 해제
    http.csrf((csrf) -> csrf.disable());

    // 기본 설정인 Session 방식은 사용하지 않고 JWT 방식을 사용하기 위한 설정
    http.sessionManagement((sessionManagement) ->
            sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    );

    http.authorizeHttpRequests((authorizeHttpRequests) ->
            authorizeHttpRequests
                    .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll() // resource 접근 허용 설정
                    .requestMatchers("/api/auth/**").permitAll() // '/api/auth/' 경로의 요청 모두 접근 허가 (회원가입, 로그인)
                    .anyRequest().authenticated() // 그 외 모든 요청 인증처리
    );

    // 필터 관리
    http.addFilterBefore(jwtAuthorizationFilter(), JwtAuthenticationFilter.class);
    http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);

    return http.build();
}
2. 필터 체인 디버깅 및 로깅 추가
  • 필터 체인이 제대로 작동하는지 확인하기 위해 JwtAuthenticationFilterJwtAuthorizationFilter에 디버그 로그를 추가했습니다.
  • 디버깅 결과, FilterChainProxy가 요청을 잘 받아들이고 있었으나, 요청이 JwtAuthorizationFilter에서 차단된 듯 보였습니다.
3. FilterChainProxy 흐름 조사
  • FilterChainProxy의 흐름을 자세히 추적하며 요청이 doFilter()를 통해 다음 필터나 컨트롤러로 전달되지 않는 문제를 확인했습니다.
  • 다양한 블로그와 문서를 참조하며 필터 체인 설정과 Spring Security의 필터 처리 방식을 다시 점검하였으나, 명확한 해답을 찾기 어려웠습니다.
4. JwtAuthorizationFilter의 doFilter() 호출 누락 발견
  • 문제는 JwtAuthorizationFilter 내부에 있는 filterChain.doFilter(request, response); 호출 위치에서 발생했습니다.
  • filterChain.doFilter()가 조건문 내부에 위치해 있어, 특정 조건을 만족하지 않으면 doFilter()가 호출되지 않았고, 결국 요청이 컨트롤러로 전달되지 않았던 것입니다.
  • 이로 인해 FilterChainProxy가 요청을 차단한 상태에서 기본 상태 코드인 200 OK와 비어 있는 응답 본문이 반환되었습니다.
@Override
protected void doFilterInternal(HttpServletRequest request,
                                HttpServletResponse response,
                                FilterChain filterChain) throws ServletException, IOException {
    String tokenValue = jwtUtil.getJwtFromHeader(request);

    if (StringUtils.hasText(tokenValue)) {
        if (!jwtUtil.validateToken(tokenValue)) {
            log.error("Token Error");
            return;
        }

        Claims info = jwtUtil.getMemberInfoFromToken(tokenValue);

        try {
            setAuthentication(info.getSubject());
        } catch (Exception e) {
            log.error(e.getMessage());
            return;
        }
    }
    filterChain.doFilter(request, response); // 조건문 밖으로 이동하여 항상 다음 필터 호출 보장
}

서버 실행 시 동작하는 순서

이 문제를 해결하는 과정에서 서버가 시작될 때 Spring Security와 필터들이 어떻게 동작하는지, 그리고 필터 체인의 흐름을 이해할 수 있었습니다.

  1. 서버 초기화: Spring Boot 애플리케이션이 시작되면 먼저 SecurityFilterChain이 초기화되며, 등록된 필터들이 차례로 설정됩니다.
  2. FilterChainProxy 설정: Spring Security는 FilterChainProxy를 통해 필터 체인을 관리하고, 각 요청에 대해 적용할 필터를 순서대로 호출합니다.
  3. 필터 순서 설정: WebSecurityConfig에서 등록한 필터들이 특정 순서대로 FilterChainProxy에 추가됩니다. 이때 UsernamePasswordAuthenticationFilter와 같은 필터를 상속받는 필터는 Spring Security의 인증 처리에 관련된 기본 필터들과 연결됩니다.
  4. 요청 수신 및 필터 체인 적용: 클라이언트가 API 요청을 보내면 FilterChainProxy가 이를 가로채어 등록된 필터들을 통과시킵니다. 이때 각 필터가 doFilter() 메서드를 통해 다음 필터로 요청을 전달하며 인증과 인가를 처리합니다.
  5. 컨트롤러로 요청 전달: 모든 필터가 통과되면 최종적으로 요청이 컨트롤러로 전달됩니다.

이번 문제에서는 필터 체인의 흐름이 doFilter() 누락으로 끊겼고, FilterChainProxy가 요청을 차단한 상태에서 200 OK 응답만 반환되었습니다.


최종 해결

조건문 안에 있던 filterChain.doFilter() 호출을 조건문 밖으로 이동한 후, 요청이 정상적으로 컨트롤러에 전달되어 회원가입 기능이 제대로 작동하였습니다. Postman 테스트에서도 요청이 잘 전달되고 JWT가 반환되는 것을 확인했습니다.


결론

사소한 실수 하나가 전체 요청 흐름에 큰 영향을 미쳤습니다. doFilter() 위치 오류로 인해 컨트롤러에 도달하지 않는 상황을 해결하면서, 필터 체인과 Spring Security의 동작 방식을 깊이 이해할 수 있었습니다. 앞으로는 비슷한 문제가 발생할 때 내가 작성한 코드부터 꼼꼼히 살피며 실수를 점검하는 것이 중요하다는 점을 느꼈습니다.

profile
서두르지 않으나 쉬지 않고

0개의 댓글