문제상황
spring security에서 filterChain을 설정하면서 "/member/signUp" url에 대한 request는 permitAll()을 해주고 싶었다.
이 과정에서 분명 permitAll()를 해주었지만 401 error가 발생하였고 이를 해결하는 과정을 포스팅한다.
본문
String[] allowUrls = {"/", "/swagger-ui/**", "/member/signUp"};
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.cors(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(request -> request
.requestMatchers(allowUrls).permitAll()
.anyRequest().authenticated())
.addFilterBefore(loginAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(config -> config
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler));
return http.build();
}
현재 springConfig 클래스의 filterChain을 설정하는 코드입니다.
swagger를 통해 request를 보냈을 시 401 에러가 발생합니다.
MDN의 설명에 따르면 401 에러는 다음과 같은 상황에서 발생한다.
401 Unauthorized :
응답 상태 코드는 요청된 리소스에 대한 유효한 인증 자격 증명이 없기 때문에 클라이언트 요청이 완료되지 않았음을 나타냅니다.
permitAll()을 했는데 401이 뜬다라..
다시 로그를 확인을 해보았습니다.
Filter를 거치면서 AuthorizationFilter에서 권한을 확인하는 로그가 찍히던 중 DB에 조회 쿼리가 나가는 것을 확인할 수 있었습니다.
스프링 요청의 처리 구조를 보면 DB에 쿼리가 나간다는 것은 요청이 FilterChain을 거쳐 Spring container의 서비스 레이어까지 도달했다는 것이다.
이 말은 FilterChain에 속하는 spring security는 이미 지나쳤다는 말이므로 permitAll() 메서드는 정상 작동하였다.
문제 지점 확인
Post /member/signUp request가 filter를 거친 후 추가로 Post /error request가 filter를 거치는 것을 볼 수 있었다.
Spring Boot는 예외가 발생하면 구체적인 Exception Handler가 존재하지 않을 시 /error 경로로 다시 에러를 재요청한다.
한번 WAS(톰캣)를 거쳤던 request가 다시 WAS(톰캣)을 거치게 되는 것이다.
이 과정에서 필터를 거치며 security filter를 거치게 되고 /error URI는 permitAll()이 되지 않았기 때문에 AccessDeniedException를 던지게된다.
최종 문제 발생 원인
ExceptionHandler를 만들어주지 않았기 때문이다.
/member/signUp으로 request를 보내면 직접 만들어둔 customException인 NotFoundException이 발생한다.
하지만 이를 만들어놓고 해당 예외를 처리해주는 ExceptionHandler를 만들지 않았기 때문에 spring boot의 기본 에러 방식 작동하였다.
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 아래와 같은 형식의 예외를 프론트에 던져줍니다
* {
* "message": "해당 부서를 찾을 수 없습니다.",
* "httpStatus": "NOT_FOUND",
* "timestamp": "2024-02-28T15:28:59.140106",
* "detail": "해당 Id의 부서가 존재하지 않습니다."
* }
*/
@ExceptionHandler
protected ResponseEntity<ErrorResponse> handleCustomException(CustomException e) {
ErrorCode errorCode = e.getErrorCode();
ErrorResponse errorResponse = ErrorResponse.of(errorCode, LocalDateTime.now());
errorResponse.setDetail(e.getMessage());
return new ResponseEntity<>(errorResponse, errorCode.getStatus());
}
}
위와 같은 GlobalExceptionHandler 클래스를 만들고 CustomException을 처리해주는 메서드를 작성해주면 NotFoundException이 404 에러로 제대로 처리되는 것을 볼 수 있다.
'spring' 카테고리의 다른 글
Java reflection을 통해 RestDocs 생성 테스트 코드 작성 시간 1/2로 줄이기 (2) | 2024.09.27 |
---|---|
[스프링] service 인터페이스 도입과 접근제한자 (1) | 2024.09.20 |
이미지 저장/조회 서버 만들기(3) - AWS Presigned URL 이미지 업로드 (0) | 2024.02.26 |
이미지 저장/조회 서버 만들기(2) - 이미지 파일 어떻게 받아오지? (0) | 2024.02.12 |
이미지 저장/조회 서버 만들기(1) - 저장 어디에..? (0) | 2024.02.11 |