[Spring] Spring Security (9) 401, 403 Error ExceptionHandling 해보기

hyewon jeong·2023년 1월 4일
0

Spring

목록 보기
17/59

🍃 9. 401, 403 Error ExceptionHandling 해보기

Spring Security 에서 인증 및 권한 부여 과정에서 발생 할 수 있는 예외 상황을 처리하기위한 커스텀 핸들러들을 구현 하는 과정으로,
Spring Security에서는 인증 또는 권한이 거부될 때 발생하는 예외들을 처리할 수 있도록 AuthenticationEntryPoint와 AccessDeniedHandler 인터페이스를 제공한다.

  • 이렇게 커스텀 핸들러를 구현하는 이유는
    응답형식 을 일관성 있게 전달 ( DTO ) 함과 동시에 , HttpServletResponse의 OutputStream을 사용하여 응답을 작성하고 있으며, ObjectMapper를 통해 DTO 객체를 JSON으로 변환하여 클라이언트에게 전달하여 이를 통해 예외 상세 정보를 응답에 포함시키지 않고 필요한 정보만 전달함으로써 보안을 강화할 수 있기 때문이다.

WebSecurityConfig

public class WebSecurityConfig {
		
		private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
  private final CustomAccessDeniedHandler customAccessDeniedHandler;
		private final UserDetailsServiceImpl userDetailsService;

				// 접근 제한 페이지 이동 설정
				// http.exceptionHandling().accessDeniedPage("/api/user/forbidden");        
		
				 // 401 Error 처리, Authorization 즉, 인증과정에서 실패할 시 처리
				http.exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint);
      
				// 403 Error 처리, 인증과는 별개로 추가적인 권한이 충족되지 않는 경우
				http.exceptionHandling().accessDeniedHandler(customAccessDeniedHandler); 
      
				return http.build();
  }

}

CustomAccessDeniedHandle.java
권한부족 403

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    private static final SecurityExceptionDto exceptionDto =
            new SecurityExceptionDto(HttpStatus.FORBIDDEN.value(), HttpStatus.FORBIDDEN.getReasonPhrase());

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
                       AccessDeniedException accessDeniedException) throws IOException{

        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setStatus(HttpStatus.FORBIDDEN.value());

        try (OutputStream os = response.getOutputStream()) {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.writeValue(os, exceptionDto);
            os.flush();
        }
    }
}
  • CustomAccessDeniedHandler는 Spring Security에서 AccessDeniedException이 발생했을 때 처리하는 역할을 합니다. AccessDeniedException은 인증은 성공했지만 권한이 부족한 경우 발생하는 예외입니다.

  • 위의 코드에서는 handle() 메서드를 오버라이드하여 AccessDeniedException이 발생했을 때 처리 로직을 구현하고 있습니다. 주요한 동작은 다음과 같습니다:

  • 응답 상태 코드 및 형식 설정: response.setStatus(HttpStatus.FORBIDDEN.value())를 사용하여 응답 상태 코드를 403 (Forbidden)으로 설정합니다. 또한, response.setContentType(MediaType.APPLICATION_JSON_VALUE)를 사용하여 응답의 컨텐츠 타입을 JSON 형식으로 설정합니다.

  • 응답 작성: response.getOutputStream()을 사용하여 응답의 출력 스트림을 가져옵니다. ObjectMapper를 사용하여 exceptionDto 객체를 JSON 형식으로 변환한 후, 출력 스트림에 작성합니다. 마지막으로, os.flush()를 호출하여 출력 스트림을 비웁니다.

  • 즉, CustomAccessDeniedHandler는 AccessDeniedException이 발생했을 때 클라이언트에게 403 Forbidden 응답을 반환하고, 그에 따른 메시지를 JSON 형식으로 전달합니다. 이를 통해 클라이언트는 권한이 부족한 상태임을 인지하고 적절한 조치를 취할 수 있습니다.

CustomAuthenticationEntryPoint.java
인증이 필요한 자원에 접근할 때 발생하는 예외인 AuthenticationException을 처리하는 역할 401

@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
    private static final SecurityExceptionDto exceptionDto =
            new SecurityExceptionDto(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());

    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException authenticationException) throws IOException {

        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setStatus(HttpStatus.UNAUTHORIZED.value());

        try (OutputStream os = response.getOutputStream()) {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.writeValue(os, exceptionDto);
            os.flush();
        }
    }
}
        

SecurityExceptionDto.java

package com.sparta.springsecurity.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class SecurityExceptionDto {

    private int statusCode;
    private String msg;

    public SecurityExceptionDto(int statusCode, String msg) {
        this.statusCode = statusCode;
        this.msg = msg;
    }
}
profile
개발자꿈나무

0개의 댓글