기록하기

ExceptionHandlerFilter 적용(1) 본문

Server/Spring Boot

ExceptionHandlerFilter 적용(1)

jjungdev 2022. 6. 19. 20:42

Spring Security + JWT 를 적용한 프로젝트에서 토큰을 검증할 때 만료된 토큰이거나 유효성 검증에서 실패할 경우 Exception 처리를 하게 된다. 하지만 Exception 처리 이후 재로그인을 하도록 유도하기 위해 alert 을 줘야하는데 이 과정에서 프론트 개발자에게 해당 메세지를 전달해야하기에 단순히 Exception 처리만 하는 것으로는 해결할수가 없었다. 특히나 Filter 처리 과정에서 발생하는 에러이기에 처리를 위해서는 다른 설계가 필요하다고 판단되어 찾아본 결과 ExceptionHandlerFilter 를 커스텀하여 생성한 뒤 SecurityConfig 에 설정을 해주면 된다고 하여 설정을 해보았다.

 

1. Security Configuration 적용 및 flow 설계

2. Spring Security + JWT + Redis 설정 및 진행 과정

3. ExceptionHandlerFilter 설계

 

Security Configuration 적용 및 flow 설계

먼저, 다음과 같은 flow 로 설계를 하였다.

 

어떤 request 가 들어올 때마다 CustomAuthenticationFilter 에서 요청을 먼저 처리하게 된다. 이때, 헤더에서 꺼내온 토큰이 redis 에서 찾은 토큰과 일치하고 유효할 때 다음 Filter 과정을 거칠 수 있게 된다. 하지만 이 과정에서 유효성 검증에 실패하게 되면 Exception 을 터뜨리는데 이때 ExceptionHandlerFilter 가 그 Exception 을 받아서 처리하는 역할을 한다.

 

또한 로그인의 경우에는 CustomLoginProcessingFilter 가 로그인 절차를 처리하도록 설계했다. 예를 들어 /login url 으로 접근하게 되면 로그인이 진행되는 것이며 이 Filter 안에서 처리를 하게 된다. 이때는 토큰이 없지만 받아 온 로그인 정보가 DB 에 있는지 검사하여 이 과정에서 문제가 발생하게 되면 Exception 을 던지게 된다. 이 역시 ExceptionHandlerFilter 가 그 Exception 을 받아서 처리하는 것이다. 

 

그렇다면, 이를 SecurityConfig 에서 어떻게 설계를 하면 되는걸까?

필터 처리의 순서만 본다면 아래와 같이 작성할 수 있다. (그 외 설정은 아주 기본적인 것만 해 둔 상태이며 2. Spring Security + JWT + Redis 설정 및 진행 과정 을 작성하며 상세하게 설명하도록 하겠다.)

 

@Slf4j
@Order(0)
@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final CustomLoginProcessingFilter customLoginProcessingFilter;
    private final CustomAuthenticationFilter customAuthenticationFilter;
    private final CustomExceptionHandlerFilter customExceptionHandlerFilter;

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("user1")
                .password("$2a$10$lwQSzZIweS0rxglCDLETtuHvGMFMdFmf6Y/ySZkeUkWk4VTZkY.Ya") //qwer1234@
                .roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().permitAll()

                .and()
                .exceptionHandling()
//                .authenticationEntryPoint()
//                .accessDeniedHandler()

                .and()
                .csrf().disable()
                .cors().disable()
                .formLogin()
                .loginProcessingUrl("/login");

        http.addFilterBefore(customLoginProcessingFilter, UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(customAuthenticationFilter, CustomLoginProcessingFilter.class)
                .addFilterBefore(customExceptionHandlerFilter, CustomAuthenticationFilter.class);
    }
}

 

가장 아래 addFilterBefore 을 보게 되면, 

지정된 필터 앞에 커스텀 필터를 추가하여 UsernamePasswordAuthenticationFilter 보다 CustomLoginProcessingFilter 를 먼저 실행하겠다는 의미를 나타낸다.

이렇게 설정을 해줌으로써 위 설계대로 진행을 할 수 있게 된다. 

 

세부적인 토큰의 유효성 검증과 redis, Exception 처리는 다음 블로그에서 작성하도록 하겠다.