Intro
JWT 토큰 값을 읽어서, 상황에 따라 인증하거나 만료되었다면 그에 따른 예외처리를 내려주는 JwtAuthenticationFilter를 만들어 SecurityConfig 설정 내에 등록해주었다.
시큐리티 필터체인에서 아래와 같이 등록하여 작동하도록 해두었는데,
.addFilterAfter(jwtAuthenticationFilter, ExceptionTranslationFilter.class)
요청이 올 때마다 자꾸 해당 필터를 두 번씩 거치는 문제가 발생했다.
Why ?
간편하게 Bean으로 등록해두고 사용하기 위해 클래스에 @Component 어노테이션을 붙여 사용했는데, 이게 바로 문제였다. 아래는 스프링 부트 도큐먼트에서 Filter에 관한 내용이다.
요약하자면, 스프링 빈으로 등록된 Servlet, Filter, Listener 인스턴스들은 내장 컨테이너에 자동 등록된다. 라는 이야기
그러니 결국 원인을 분석해보자면 똑같은 필터가 자동으로, 수동으로 총 2번 등록되었다는 말이다. 자동으로 등록된 필터의 문제점은 URL 패턴 없이 그냥 모든 요청을 대상으로 다 적용이 되며 특히 순서를 보장하기도 굉장히 까다롭다는 것이다.
Solve
필터가 자동으로 등록되지 않도록 @Component 어노테이션을 제거한다.
@Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final String AUTHENTICATION_HEADER = "Authorization";
private static final String AUTHENTICATION_SCHEME = "Bearer ";
private final JwtTokenProvider jwtTokenProvider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
String accessToken = resolveToken(request);
if (hasText(accessToken)) {
jwtTokenProvider.validateAccessToken(accessToken);
...
스프링 시큐리티 설정파일에서는 해당 Filter를 생성자를 통해 생성해 필터체인에 등록하면 되는데, 필터에 필요한 객체들은 해당 설정파일 객체의 생성자를 통해 주입받고 이후 수동으로 원하는 위치에 해당 필터를 적용하면 된다.
@Bean
public SecurityFilterChain securityFilterChainDefault(HttpSecurity http) throws Exception {
configureCommonSecuritySettings(http);
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest()
.authenticated()
)
.addFilterAfter(new JwtAuthenticationFilter(jwtTokenProvider), ExceptionTranslationFilter.class)
...
시큐리티는 복잡하고 어려울 뿐만 아니라, 사용하기에 쉽지 않기 때문에 충분히 학습이 필요하다는 것을 느끼게 되었다.