[Spring] JwtAuthenticationFilter OncePerRequestFilter 특정 url 제외하기

nayu1105·2023년 8월 21일
0

Spring

목록 보기
3/3

Jwt을 사용하여 사용자 인증을 하는 서비스를 만들면서 Filter를 만들어서 그 기능을 자동으로 실행되도록 하려고 했다.

이를 위해 SpringSecurity FilterChain에 JwtAuthenticationFilter 을 추가하였다.


SecurityConfig 코드는 아래와 같다.


@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
public class SecurityConfig {

    private final JwtProvider jwtProvider;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()

            .authorizeRequests() // 요청에 대한 사용권한 체크
            .antMatchers("/auth/signup", "/auth/login").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin().disable()
            .logout().disable();

        http.addFilterBefore(new JwtAuthenticationFilter(jwtProvider),
            UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

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


}

JwtAuthenticationFilter는 아래와 같다.


@Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtProvider jwtProvider;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
        FilterChain filterChain) throws ServletException, IOException {
        log.info("JwtAuthenticationFilter : jwt 인증 처리");

        String jwtToken = parseJwt(request);
        log.info("jwtToken = {}", jwtToken);
        if (jwtToken != null) {
            if (jwtProvider.validateToken(jwtToken)) {
                Authentication auth = jwtProvider.getAuthentication(jwtToken);
                SecurityContextHolder.getContext().setAuthentication(auth);
            } else {
                log.info("code : {}, message : {}", StatusCode.TOKEN_EXPIRE.getCode(),
                    StatusCode.TOKEN_EXPIRE.getMessage());
                throw new TokenExpireException();
            }

        } else {
            log.info("code : {}, message : {}", StatusCode.FORBIDDEN.getCode(),
                StatusCode.FORBIDDEN.getMessage());
            throw new ForbiddenException();
        }
        filterChain.doFilter(request, response);
    }

    private String parseJwt(HttpServletRequest request) {
        String headerAuth = request.getHeader("Authorization");
        if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
            return headerAuth.substring(7, headerAuth.length());
        }
        return null;
    }
}

그러나 SecuritConfig에 특정 url은 모든 권한을 허용해주는 코드가 작동하지 않았다.

.antMatchers("/auth/signup", "/auth/login").permitAll()

내가 원했던 것은 이 url 은 Filter를 거치지 않는 것이였는데, 실제로는 Filter는 지나가고 권한은 허용해주는 것이였다.

그래서 다음 url이 Filter를 지나가지 않도록 하기 위해서는 OncePerRequestFiltershouldNotFilter 함수를 Override해야 했다.

바뀐 JwtAuthenticationFilter는 아래와 같다.

Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtProvider jwtProvider;

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        String[] excludePath = {"/auth/signup", "/auth/login"};
        // 제외할 url 설정
        String path = request.getRequestURI();
        return Arrays.stream(excludePath).anyMatch(path::startsWith);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
        FilterChain filterChain) throws ServletException, IOException {
        log.info("JwtAuthenticationFilter : jwt 인증 처리");

        String jwtToken = parseJwt(request);
        log.info("jwtToken = {}", jwtToken);
        if (jwtToken != null) {
            if (jwtProvider.validateToken(jwtToken)) {
                Authentication auth = jwtProvider.getAuthentication(jwtToken);
                SecurityContextHolder.getContext().setAuthentication(auth);
            } else {
                log.info("code : {}, message : {}", StatusCode.TOKEN_EXPIRE.getCode(),
                    StatusCode.TOKEN_EXPIRE.getMessage());
                throw new TokenExpireException();
            }

        } else {
            log.info("code : {}, message : {}", StatusCode.FORBIDDEN.getCode(),
                StatusCode.FORBIDDEN.getMessage());
            throw new ForbiddenException();
        }
        filterChain.doFilter(request, response);
    }

    private String parseJwt(HttpServletRequest request) {
        String headerAuth = request.getHeader("Authorization");
        if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
            return headerAuth.substring(7, headerAuth.length());
        }
        return null;
    }
}

shouldNotFilter 에서 설정한 excludePath 는 해당 Filter의 동작을 수행하지 않는 것을 확인했다.

다음은 authenticationEntryPoint에 대해 정리하겠다.

0개의 댓글