PermitAll() 적용 안되는 경우

형석이의 성장일기·2024년 4월 3일
0

Spring Security

목록 보기
4/6

PermitAll() 이란?

  • 해당 URL 에 대한 모든 요청을 허용하는 메서드임
  • 주로 Spring Security 에선 filterChain() 설정 메서드에서 사용함

SecurityConfig

@Slf4j
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    private final JwtTokenProvider jwtTokenProvider;

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

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

        http.authorizeHttpRequests(
                authorize -> authorize
                        .requestMatchers("/api/v1/user/login").permitAll()
                        .requestMatchers("/api/v1/user/join").permitAll()
                        .requestMatchers("/api/v1/user/reissue").permitAll()
                        .anyRequest().authenticated()
        ).apply(new JwtSecurityConfig(jwtTokenProvider));
        
        return http.build();
    }

}
  • 요청을 허용할 URL은 회원가입, 로그인, Access Token 재발급 URL임
  • 내가 예상한 결과는 저 3개의 요청만 필터를 거치지 않고 바로 컨트롤러로 가는 거임
  • 근데 계속 헤더가 없다는 403 결과가 나옴

이 에러를 보자마자 ‘어..? 내가 permitAll() 에 대해 잘 못 생각하고 있나..?’ 라고 느낌

근데 위에서 내가 “모든 요청은 허용한다” 라고 했는데..?

이 말은 반은 맞고 반은 틀림

왜냐하면 permitAll() 에 설정을 해도, Spring Security 에 있는 Filter Chain 은 반드시 거치기 때문임

그러면 왜 많은 블로그에서 permitAll() 이 URL 에 해당하는 요청을 허용한다는 의미일까?

그 이유는 요청이 Filter Chain 에 들어오지만, permitAll() 에 지정된 URL 일 경우, SecurityContextHolder 에 보관되어 있는 Authentication 객체에 해당 요청을 보낸 유저 정보가 없더라도 (당연히 없겠죠? 헤더에 토큰을 담고 요청을 보내지 않았으니까!!) 정상 흐름으로 처리하여 컨트롤러에 요청을 보내는 것임

뭐 어떻게 보면 요청을 허용하는 건 맞는듯?

근데 내 코드에선 에러가 뜨는 이유는, 아래의 코드를 보면

JwtTokenProvider

@Slf4j
@Component
@RequiredArgsConstructor
public class JwtTokenProvider {

		...
		
    /**
     * Access Token 검증
     */
    public boolean validateToken(String token){
        try{
            Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
            return true;
        } catch(ExpiredJwtException e) {
            log.error("Token 만료");
            throw new RuntimeException("Token 만료. 재발급 필요");
        } catch(JwtException e) {
            log.error("잘못된 Access Token 타입");
            throw new RuntimeException("잘못된 Access Token 타입");
        } catch (IllegalArgumentException e) {
            log.error("헤더가 비어있음");
            throw new RuntimeException("헤더가 비어있음");
        }
    }
}
  • validateToken : 토큰 검증 메서드
catch(ExpiredJwtException e) {
       log.error("Token 만료");
       throw new RuntimeException("Token 만료. 재발급 필요");
} catch(JwtException e) {
       log.error("잘못된 Access Token 타입");
       throw new RuntimeException("잘못된 Access Token 타입");
} catch (IllegalArgumentException e) {
       log.error("헤더가 비어있음");
       throw new RuntimeException("헤더가 비어있음");
}

이런 식으로 검증을 시도하는 부분에서 직접 예외를 던짐

이런 식으로 해버리면 당연히 filter.doFilter() 로 넘어가서, 컨트롤러로 넘어가는 게 아니고, ExceptionTranslationFilter 로 넘어가는 것


해결 방법

물론 JwtFilter 의 doFilterInternal 메서드에서 따로 JwtAuthenticationException 과 같은 예외로 catch 해도 되긴 하지만 내가 상속받은 상위 클래스인 OncePerRequestFiltershouldNotFilter() 라는 메서드가 존재함

말 그대로 true 를 리턴하면 필터를 거치지 않는 것임

JwtFilter

@Slf4j
@RequiredArgsConstructor
public class JwtFilter extends OncePerRequestFilter {

    private final JwtTokenProvider jwtTokenProvider;

    private static final List<String> EXCLUDE_URL =
            Collections.unmodifiableList(
                    Arrays.asList(
                            "/api/v1/user/login",
                            "/api/v1/user/join",
                            "/api/v1/user/reissue"
                    ));
                    
    ...
    
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        return EXCLUDE_URL.stream().anyMatch(exclude -> exclude.equalsIgnoreCase(request.getServletPath()));
    }
    
    ...
    
}
  • OncePerRequestFilter : 모든 서블릿에 일관된 요청을 처리하기 위해 사용하는 필터용 추상화 클래스
  • EXCLUDE_URL : 필터를 거치면 안되는 URL 을 담은 리스트
  • shouldNotFilter : OncePerRequestFilter 에 선언되어 있는 메서드로 지정된 요청의 필터링을 피하기 위해 true를 반환함

shouldNotFilter() 메서드를 재정의 함으로써 JwtFilter 으로 요청이 와도 URL 이 EXCLUDE_URL 리스트에 존재하면 바로 true 를 반환함으로써 필터를 벗어나 바로 컨트롤러로 이동함

profile
이사중 .. -> https://gudtjr2949.tistory.com/

0개의 댓글

관련 채용 정보