
JWT를 검증하기 위한 필터를 구현하였는데, 특정 상황에서 필터가 2번 실행되는 경우가 발생하였다.
Spring에 특화된 Filter 구현체인 GenericFIlterBean을 이용하여 JWT token 값이 유효하지 않을떄 에러를 Response하는 필터를 구현 하였다.
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends GenericFilterBean {
private final JwtTokenResolver jwtTokenResolver;
private final JwtTokenValidator jwtTokenValidator;
private final ApiResponseBuilder apiResponseBuilder;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("필터 실행");
// 1. Request Header에서 JWT 토큰 추출
String token = jwtTokenResolver.extractToken((HttpServletRequest) request);
// 2. validateToken 으로 토큰 유효성 검사
if (token != null) {
if (jwtTokenValidator.validateToken(token)){
// 토큰이 유효할 경우 토큰에서 Authentication 객체를 가져와 SecurityContext에 저장
Authentication authentication = jwtTokenResolver.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
else{
apiResponseBuilder.buildErrorResponse((HttpServletResponse) response, BAD_REQUEST, INVALID_JWT);
}
}
else{
chain.doFilter(request, response);
}
}
}
필터 등록은 SecurityFilterChain에 등록해주었다
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
{생략}
.addFilterBefore(customGenericFilterBean, UsernamePasswordAuthenticationFilter.class)
.build();
}
중복 ID가 존재하면 에러를 반환하고 아니면 회원가입을 하는 단순한 회원가입 API를 호출 하면 Filter가 2번 실행된다.

스프링부트는 Filter가 스프링 빈에 등록되어있으면 이를 필터에 등록해준다. 하지만 지금 현재 구현한 코드상엔 addFilterBefore을 이용하여 Filter체인에 등록했기에 같은 필터가 두번 등록된 것이다.
한 서블릿 요청에 한번에 Filter 호출만을 하는 OncePerRequestFilter를 이용하여 구현한다.
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenResolver jwtTokenResolver;
private final JwtTokenValidator jwtTokenValidator;
private final ApiResponseBuilder apiResponseBuilder;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 1. Request Header에서 JWT 토큰 추출
String token = jwtTokenResolver.extractToken((HttpServletRequest) request);
// 2. validateToken 으로 토큰 유효성 검사
if (token != null) {
if (jwtTokenValidator.validateToken(token)){
// 토큰이 유효할 경우 토큰에서 Authentication 객체를 가져와 SecurityContext에 저장
Authentication authentication = jwtTokenResolver.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
else{
apiResponseBuilder.buildErrorResponse((HttpServletResponse) response, BAD_REQUEST, INVALID_JWT);
}
}
else{
filterChain.doFilter(request, response);
}
}
}