JwtAuthenticationFilter
JwtAuthenticationFilter
는 JWT 토큰으로 인증하고 SecurityContextHolder
에 추가하는 필터를 설정하는 클래스이다.
스프링 부트에서는 Filter를 여러 방법으로 구현할 수 있는데, 가장 편한 구현 방법은 Filter를 상속받아 사용하는 것이다.
대표적으로 GenericFilterBean
과 OncePerRequestFilter
두 가지 상속 객체를 사용한다.
GenericFilterBean을 사용한 구현
public class JwtAuthenticationFilter extends GenericFilterBean {
private final JwtTokenProvider jwtTokenProvider;
public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
String token = jwtTokenProvider.resolveToken((HttpServletRequest) servletRequest);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication authentication = jwtTokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(servletRequest, servletResponse);
}
}
- 토큰 추출: jwtTokenProvider.resolveToken 메서드를 호출하여 요청 헤더 또는 파라미터에서 JWT 토큰을 가져온다.
- 토큰 검증: 가져온 토큰이 null이 아니고 유효한지 jwtTokenProvider.validateToken 메서드를 이용하여 검증한다.
- 인증 설정: 유효한 토큰일 경우 jwtTokenProvider.getAuthentication 메서드를 이용하여 토큰으로부터 인증 정보(Authentication) 객체를 생성한다. 생성된 인증 정보를 SecurityContextHolder.getContext().setAuthentication 메서드를 통해 Spring Security 컨텍스트에 설정한다.
- 필터 체인 진행: filterChain.doFilter 메서드를 호출하여 다음 필터로 요청을 전달한다.
OnePerRequestFilter을 사용한 구현
public class JwtAuthenticationFilter extends OncePerRequestFilter {
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);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication authentication = jwtTokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(servletRequest, servletResponse);
}
}
- 토큰 추출: jwtTokenProvider.resolveToken 메서드를 호출하여 요청 헤더 또는 파라미터에서 JWT 토큰을 가져온다.
- 토큰 검증: 가져온 토큰이 null이 아니고 유효한지 jwtTokenProvider.validateToken 메서드를 이용하여 검증한다.
- 인증 설정: 유효한 토큰일 경우 jwtTokenProvider.getAuthentication 메서드를 이용하여 토큰으로부터 인증 정보(Authentication) 객체를 생성한다. 생성된 인증 정보를 SecurityContextHolder.getContext().setAuthentication 메서드를 통해 Spring Security 컨텍스트에 설정한다.
- 필터 체인 진행: filterChain.doFilter 메서드를 호출하여 다음 필터로 요청을 전달한다.
GenericFilterBean vs OncePerRequestFilter
- GenericFilterBean은 기존 필터에서 가져올 수 없는 스프링의 설정 정보를 가져올 수 있게 확장된 추상 클래스이다.
- 다만 서블릿은 사용자의 요청을 받으면 서블릿을 생성해서 메모리에 저장해두고 동일한 클라이언트의 요청을 받으면 재활용하는 구조여서, GenericFilterBean을 상속받으면
RequestDispathcer
에 의해 다른 서블릿으로 디스패치되면서 필터가 두 번 실행 되는 현상이 발생할 수 있다. GenericFilterBean
은 매 요청마다 실행된다.- 이 문제를 해결하기 위해 OncePerRequestFilter가 등장했다.
- 이 클래스는 GenericFilterBean을 상속받고 있지만, 이 클래스를 상속받아 구현한 Filter는 매 요청마다 한 번만 실행되게끔 구현되어 있다.
1. 요청당 실행 횟수
GenericFilterBean
: 매 요청마다 실행OncePerRequestFilter
: 요청당 한 번만 실행
2. 동작 방식
GenericFilterBean
:doFilter
메서드를 직접 구현FilterChain.doFilter
를 호출하여 다음 필터로 요청을 전달- 서블릿마다 매 요청마다 호출
OncePerRequestFilter
:doFilterInternal
메서드를 구현doFilter
메서드에서doFilterInternal
메서드를 한 번만 호출- 스레드 기반으로 요청당 한 번만 호출
3. 용도
GenericFilterBean
:- 요청 전/후 처리, 로그 기록, 인증/권한 처리 등 다양한 용도로 사용
- 간단한 필터 구현에 적합
OncePerRequestFilter
:- 중복 실행을 방지해야 하는 필터에 적합
- 예: 인증 필터, CSRF 토큰 검증 필터
4. 상속 관계
GenericFilterBean
:javax.servlet.Filter
인터페이스를 구현OncePerRequestFilter
:GenericFilterBean
을 상속
5. 주의 사항
GenericFilterBean
:- 매 요청마다 실행되므로 성능에 영향을 줄 수 있다.
- 스레드 안전하지 않을 수 있다.
OncePerRequestFilter
:- 요청당 한 번만 실행되므로 성능 향상에 도움
- 스레드 안전
참고
- Google Gemini
- 스프링 부트 핵심 가이드 "스프링 부트를 활용한 애플리케이션 개발 실무" , 장정우, 2022
- https://github.com/wikibook/springboot/tree/main/chapter13_security
'Development > Spring' 카테고리의 다른 글
[Spring] Spring Security + JWT: UserDetails와 로그인, 회원가입 구현하기 (0) | 2024.04.15 |
---|---|
[Spring] Spring Security + JWT: SecurityFilterChain, AccessDeniedHandler, AuthenticationEntryPoint (0) | 2024.04.07 |
[Spring] Spring Security + JWT: JwtTokenProvider (0) | 2024.04.06 |
[Spring] JWT와 Spring Security (1) | 2024.03.27 |
[Spring] 인증과 권한 부여, Spring Security (0) | 2024.03.22 |