WIL/웹 개발

JwtFilter는 어디에 배치해야 할까?

아크리미츠 2024. 10. 19. 20:07

스프링 시큐리티(Spring Security)를 사용할 때, 요청이 서버로 들어오고 나가는 과정을 필터 체인이 담당한다. 이 필터 체인에서 JwtFilter는 JWT 토큰을 인증하고, ExceptionTranslationFilter는 인증 과정에서 발생하는 예외를 처리한다. 그렇다면, JwtFilter는 필터 체인에서 어디에 배치해야 할까?

1. JWT 필터(JwtFilter)란?

JwtFilter는 클라이언트가 요청에 포함한 JWT(JSON Web Token)를 확인하고, 유효하다면 해당 사용자의 인증 정보를 스프링 시큐리티의 SecurityContextHolder에 저장하는 역할을 한다. 이를 통해 클라이언트 요청이 인증된 사용자로부터 온 것임을 보장할 수 있다.

public class JwtFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 
                                    FilterChain filterChain) throws ServletException, IOException {
        String token = resolveToken(request);

        if (token != null && JwtUtil.validateToken(token)) {
            Authentication auth = JwtUtil.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(auth);
        }

        filterChain.doFilter(request, response);
    }

    private String resolveToken(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        return bearerToken != null && bearerToken.startsWith("Bearer ") ? bearerToken.substring(7) : null;
    }
}

2. 예외 처리 필터(ExceptionTranslationFilter)란?

ExceptionTranslationFilter는 스프링 시큐리티에서 발생하는 인증 및 권한 예외를 처리하는 필터이다. 인증되지 않은 사용자가 보호된 리소스에 접근하려고 할 때 AuthenticationException이나 AccessDeniedException이 발생할 수 있는데, 이때 ExceptionTranslationFilter가 해당 예외를 처리하여 적절한 응답을 반환한다. 예를 들어, 인증되지 않은 사용자가 보호된 페이지에 접근하려 할 때 401 Unauthorized 응답을 반환하거나 로그인 페이지로 리다이렉트할 수 있다.

3. 결론

JwtFilter는 반드시 ExceptionTranslationFilter보다 먼저 실행되어야 한다. 그 이유는 다음과 같다:

  1. JWT 인증 처리: JwtFilter는 클라이언트가 보낸 JWT 토큰을 먼저 검증하고, 인증 정보를 SecurityContextHolder에 설정한다. 따라서 예외 처리 필터보다 먼저 실행되어야만 인증을 시도할 수 있다.
  2. 예외 처리: JwtFilter에서 인증 정보가 설정되지 않으면 이후 요청에서 예외가 발생할 수 있다. 이때 ExceptionTranslationFilter가 그 예외를 처리하여 적절한 응답을 반환한다.
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // JwtFilter를 ExceptionTranslationFilter보다 먼저 실행하도록 추가
        http.addFilterBefore(new JwtFilter(), ExceptionTranslationFilter.class);

        http
            .authorizeRequests()
                .antMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin() // 로그인 설정
                .loginPage("/login")
                .permitAll()
            .and()
            .logout()
                .permitAll();
    }
}