JwtAuthenticationFilter는 JWT 토큰으로 인증하고 SecurityContextHolder에 추가하는 필터를 설정하는 클래스이다.
@RequiredArgsConstructor
@Slf4j
public class JwtTokenFilter extends OncePerRequestFilter { //api 요청을 할 때 한 번만 인증을 거친다??
private final UserService userService;
private final String secretKey;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//Token에서 Claim 꺼내기
final String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION);
if(authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")){ //header에 AUTHORIZATION이 없거나, Bearer로 시작하지 않으면 filter
log.error("header가 없거나, 형식이 틀립니다. - {}", authorizationHeader);
filterChain.doFilter(request, response);
return;
}
String token;
try {
token = authorizationHeader.split(" ")[1].trim();
} catch (Exception e) {
log.error("토큰을 분리하는데 실패했습니다. - {}", authorizationHeader);
filterChain.doFilter(request, response);
return;
}
log.info("token : {}", token);
//토큰이 Valid한지 확인하기
if(JwtTokenUtil.isExpired(token, secretKey)){
filterChain.doFilter(request, response);
return;
}
//userName 넣기, 문 열어주기
String userName = JwtTokenUtil.getUserName(token, secretKey);
log.info("userName : {}", userName);
User user = userService.getUserByUserName(userName);
//AuthenticationToken 만들기
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getUserName(), null, List.of(new SimpleGrantedAuthority(user.getRole().name())));
//디테일 설정하기
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
}
}
GenericFilterBean vs OncePerRequestFilterGenericFilterBean은 기존 필터에서 가져올 수 없는 스프링의 설정 정보를 가져올 수 있게 확장된 추상 클래스이다. 서블릿은 사용자의 요청을 받으면 서블릿을 생성해서 메모리에 저장해두고 동일한 클라이언트의 요청을 받으면 재활용하는 구조여서 GenericFilterBean을 상속받으면 RequestDispatcher에 의해 다른 서블릿으로 디스패치되면서 필터가 두 번 실행되는 현상이 발생할 수 있다.
이 같은 문제를 해결하기 위해 등장한 것이 OncePerRequestFilter이며, 이 클래스도 GenericFilterBean을 상속받고 있지만, 이 클래스를 상속받아 구현한 필터는 매 요청마다 한 번만 실행되게끔 구현된다.
Filter base class that aims to guarantee a single execution per request dispatch, on any servlet container. It provides a doFilterInternal method with HttpServletRequest and HttpServletResponse arguments.
어느 서블릿 컨테이너에서나 요청 당 한 번의 실행을 보장하는 것을 목표로 한다.doFilterInternal메소드와 HttpServletRequest와 HttpServletResponse인자를 제공한다.
중요한 점은 요청 당 한번의 실행을 보장한다는 것이다.
서블릿이 실행되는 동안 다른 서블릿에 요청이 올 수도 있다.
예를들어, 어느 필터에서 헤더를 확인 한 후 특정 url로 포워딩 시킨다고 가정하자.
이때 예외가 발생하지 않았다면, url로 포워딩 시키는 것 자체가 서블릿 실행 중 요청이 온 것이다.
OncePerRequestFilter를 사용하지 않았다면 앞서 거친 필터들을 또 한번 거칠 것이고, 쓸데없는 자원만 낭비하는 셈이다.
결국 동일한 request안에서 한번만 필터링을 할 수 있게 해주는 것이 OncePerRequestFilter의 역할이고 보통 인증 또는 인가와 같이 한번만 거쳐도 되는 로직에서 사용한다.
인증 또는 인가를 거치고나서 특정 url로 포워딩하면, 요청이 들어왔으니 인증 및 인가필터를 다시 실행시켜야 하지만, OncePerRequestFilter를 사용함으로써 인증이나 인가를 한번만 거치고 다음 로직을 진행할 수 있도록 한다.
출처 : https://emgc.tistory.com/119
OncePerRequestFilter에서 구현하는 메서드이다. doFilter()는 다음 filter-chain을 실행하는 것이며, 마지막 filter-chain인 경우 Dispatcher Servlet이 실행된다.

참고: https://pgnt.tistory.com/102
UsernamePasswordAuthenticationToken
1) 사용자가 사용자 이름과 암호를 제출하면 UsernamePasswordAuthenticationFilter는 HttpServletRequest에서 사용자 이름과 암호를 추출하여 일종의 인증인 UsernamePasswordAuthenticationToken을 생성합니다.
2) 다음으로 UsernamePasswordAuthenticationToken이 인증을 위해 AuthenticationManager로 전달됩니다. AuthenticationManager의 모양에 대한 세부 정보는 사용자 정보가 저장되는 방식에 따라 다릅니다.
3) 인증이 실패하면,
4) 인증에 성공하면,
SecurityContextHolder에서 설정됩니다.

