[Spring] JwtAuthenticationFilter 구현

WOOK JONG KIM·2022년 11월 9일
0
post-thumbnail
post-custom-banner

JwtAuthenticationFilter는 JWT 토큰으로 인증하고 SecurityContextHolder에 추가하는 필터를 설정하는 클래스

public class JwtAuthenticationFilter extends OncePerRequestFilter {
    
    private final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
    private final JwtTokenProvider jwtTokenProvider;
    
    public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider){
        this.jwtTokenProvider = jwtTokenProvider;
    }
    
    @Override
    protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
        String token = jwtTokenProvider.resolveToken(servletRequest);
        LOGGER.info("[doFilterInternal] token 값 추출 완료, token: {}", token);
        
        LOGGER.info("[doFilterInternal] token 값 유효성 체크 시작");
        if(token != null && jwtTokenProvider.validateToken(token)){
            Authentication authentication = jwtTokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            LOGGER.info("[doFilterInternal] token 값 유효성 체크 완료");
        }
        
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

스프링 부트에서는 필터를 여러 방법으로 구현하는데, 가장 편한 구현 방법은 필터를 상속받아 사용하는 것
-> 대표 적인 상속 객체는 GenericFilterBean & OncePerRequestFilter

GenericFilterBean은 기존 필터에서 가져올 수 없는 스프링의 설정 정보를 가져올 수 있게 확장된 추상 클래스
다만 서블릿은 사용자의 요청을 받으면 서블릿을 생성해서 메모리에 저장해두고 동일한 클라이언트의 요청을 받으면 재활용하는 구조여서 이를 상속받으면 RequestDispatcher에 의해 다른 서블릿으로 디스패치 되면서 필터가 두번 실행될 수 있음
-> 이를 해결하기 위해 등장한 것이 OncePerRequestFilter이며 이 또한 GenericFilterBean을 상속받고 있음

doFilter()는 서블릿을 실행하는 메서드인데, 이를 기존으로 앞에 작성한 코드는 서블릿 실행되기 전에 실행되고, 뒤에 작성한 코드는 서블릿 실행된 후 실행

메서드 로직을 보면 JwtTokenProvider를 통해 servletRequest에서 토큰을 추출하고, 토큰에 대한 유효성 검사
-> 토큰이 유효하다면 Authentication 객체를 생성해서 SecurityContextHolder에 추가

GenericFilterBean을 상속받아 구현한 경우

public class JwtAuthenticationFilter extends GenericFilterBean{
    
    private final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
    private final JwtTokenProvider jwtTokenProvider;
    
    public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider){
        this.jwtTokenProvider = jwtTokenProvider;
    }
    
    @Override
    protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
        String token = jwtTokenProvider.resolveToken((HttpServletRequest) servletRequest);
        LOGGER.info("[doFilterInternal] token 값 추출 완료, token: {}", token);
        
        LOGGER.info("[doFilterInternal] token 값 유효성 체크 시작");
        if(token != null && jwtTokenProvider.validateToken(token)){
            Authentication authentication = jwtTokenProvider.getAuthentication(token);
            SecurityContextHolder.getContext().setAuthentication(authentication);
            LOGGER.info("[doFilterInternal] token 값 유효성 체크 완료");
        }
        
        filterChain.doFilter(servletRequest, servletResponse);
    }
}
profile
Journey for Backend Developer
post-custom-banner

0개의 댓글