인프런/스프링 시큐리티 완전 정복 [6.x 개정판]

15) 예외처리 - exceptionHandling(), ExceptionTranslationFilter

backend dev 2024. 10. 24.

exceptionHandling()

 

• 예외 처리는 필터 체인 내에서 발생하는 예외를 의미하며

크게 인증예외(AuthenticationException) 인가예외(AccessDeniedException)로 나눌 수 있다

 

• 예외를 처리하는 필터로서 ExceptionTranslationFilter 가 사용 되며 사용자의 인증 및 인가 상태에 따라

로그인 재시도, 401, 403 코드 등으로 응답할 수 있다

 

 

예외 처리 유형

AuthenticationException [ 인증 예외 ] 

 

해당 예외가 발생하면 ExceptionTranslationFilter가 진행하는 과정

 

1. SecurityContext에서 인증 정보 삭제 

기존의 Authentication 이 더 이상 유효하지 않다고 판단하고 Authentication 을 초기화 한다


2. AuthenticationEntryPoint 호출
 AuthenticationException 이 감지되면 필터는 authenticationEntryPoint 를 실행하고 이를 통해 인증 실패를 공통적으로

처리할 수 있으며 일반적으로 인증을 시도할 수 있는 화면으로 이동한다


3. 인증 프로세스의 요청 정보를 저장하고 검색
RequestCache & SavedRequest­ 인증 프로세스 동안 전달되는 요청을 세션 혹은 쿠키에 저장
사용자가 인증을 완료한 후 요청을 검색하여 재 사용할 수 있다. 기본 구현은 HttpSessionRequestCache 이다

 

 

AccessDeniedException [ 인가 예외 ] 

해당 예외가 발생하면 ExceptionTranslationFilter가 진행하는 과정

 

AccessDeniedHandler 호출
AccessDeniedException이 감지되면 필터는 사용자가 익명 사용자인지 여부를 판단하고

익명 사용자인 경우 인증예외처리가 실행되고 익명 사용자가 아닌 경우 필터는 AccessDeniedHandler 에게 위임한다

 

exceptionHandling() API

 

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.exceptionHandling(exception -> exception
            .authenticationEntryPoint((request, response, authException) -> { // 커스텀하게 사용할 AuthenticationEntryPoint 를 설정한다
                System.out.println(authException.getMessage());
            })
            .accessDeniedHandler((request, response, accessDeniedException) -> { // 커스텀하게 사용할 AccessDeniedHandler 를 설정한다
                System.out.println(accessDeniedException.getMessage());
            })
    );
    return http.build();
}

 

 

AuthenticationEntryPoint 는 인증 프로세스마다 기본적으로 제공되는 클래스들이 설정된다

 

- UsernamePasswordAuthenticationFIlter가 인증을 처리하다가 실패해서 예외를 던지면

해당 예외를 처리하기위해 LoginUrlAuthenticationEntryPoint가 호출되어 사용자를 로그인 페이지로 리다이렉트시켜서 인증을 다시 시도할 수 있게한다.

 

- BasicAuthenticationFilter가 HTTP Basic 인증 처리를 하다가 실패해서 예외를 던지면

해당 예외를 처리하기위해 BasicAuthenticationEntryPoint 가 호출되어 사용자를 로그인 페이지로 리다이렉트하는

대신, HTTP 상태 코드 401 (Unauthorized)를 응답으로 반환한다.

 

- 아무런 인증 프로세스가 설정 되지 않으면 기본적으로 Http403ForbiddenEntryPoint가 사용된다

[ 403 Forbidden 응답이 반환된다. ]

 

- 사용자 정의 AuthenticationEntryPoint 구현이 가장 우선적으로 수행되며 이 때는 기본 로그인 페이지 생성이 무시된다

 

AccessDeniedHandler 는 기본적으로 AccessDeniedHandlerImpl 클래스가 사용된다

 

 

예시코드)

@EnableWebSecurity
@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {


        http.authorizeHttpRequests(auth -> auth
                        .requestMatchers("/login").permitAll()
                        .requestMatchers("/admin").hasRole("ADMIN")
                        .anyRequest().authenticated())
                .formLogin(Customizer.withDefaults())
                .exceptionHandling(exception -> exception
                        .authenticationEntryPoint(new AuthenticationEntryPoint() {
                            @Override
                            public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
                                System.out.println("exception = " + exception);
                                response.sendRedirect("/login");
                            }
                        })
                        .accessDeniedHandler(new AccessDeniedHandler() {
                            @Override
                            public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                                System.out.println("exception = " + exception);
                                response.sendRedirect("/denied");

                            }
                        })
                );

        return http.build();

    }

 


ExceptionTranslationFilter

 

예외처리 흐름도

15) 예외처리 - exceptionHandling(), ExceptionTranslationFilter -  ExceptionTranslationFilter  -  AccessDeniedException [ 인가 예외 ]  - 예외처리 흐름도

[사용자는 인증을 받지않은 상태, /user는 ADMIN ROLE을 가진 사용자만 접근 가능한 엔드포인트라고 가정한다.]

 

AuthorizationFilter는 인가관련 필터이다. 

해당 http request를 한 클라이언트의 권한을 가지고 해당 엔드포인트에 접근할 수 있는지 확인한다.

 

위와같은 가정을 가지므로 AccessDeniedException이라는 인가관련 예외를 던진다. 

 

그 예외를 ExceptionTranslationFilter가 처리한다.

 

AccessDeniedException가 발생했을때 클라이언트가 익명사용자인지,

기억하기인증 [remeberMe]으로 로그인한건지 판단해서 둘중 하나라도 맞다면 AuthenticationException을 발생시킨다.

 

기억하기인증[자동로그인]으로 로그인했는데도 불구하고 해당 사용자가 권한이 없다면

AuthenticationException을 발생시키는 이유는 기억하기 인증은 쿠키를 기반으로한 낮은 보안레벨의 인증이기 때문이다. 

 

Remember-Me 로그인 (보안 레벨 낮음):

  • 쿠키 기반 자동 인증 방식입니다. 사용자가 로그인할 때 'Remember Me' 옵션을 체크하면 이후 쿠키를 통해 자동으로 인증이 이루어집니다.
  • 이 방식은 보안 수준이 낮기 때문에, 중요하지 않은 리소스(일반 콘텐츠 등)에는 접근을 허용할 수 있지만, 중요한 작업에는 제약이 있습니다.
  • 중요한 작업(예: 계정 정보 수정, 결제 등)을 시도할 때는 추가 인증을 요구하는 것이 보안적인 관점에서 매우 중요합니다.

자동로그인을 한 사용자가 권한이 있더라도, 해당 작업이 중요한 작업이라면 추가 인증을 요구할 수 도 있다.

[ 설계에 따라 다른듯하다. ]

 

그래서 결국 AuthenticationException가 발생하면

SecurityContext의 authentication[인증객체]를 null로 설정한다.

HttpSessionRequestCache를 이용하여 Session에 사용자의 요청관련 정보를 저장한다.

AuthenticationEntiryPoint를 통해 인증 실패 이후 작업을 진행한다. 

 

[RequestCache는 인터페이스이고 기본 구현체는 HttpSessionRequestCache 이며 

SavedRequest 도 인터페이스이고 기본 구현체는 DefaultSavedRequest 이다.]

 

HttpSessionRequestCache 는 DefaultSavedRequest 객체를 세션에 저장하는 역할을 하며
DefaultSavedRequest 는 현재 클라이언트의 요청과정 중에 포함된 쿠키, 헤더, 파라미터 값들을 추출하여 보관하는 역할을 합니다.

즉 현재 클라이언트의 요청 과정에서 생성되거나 참조되는 모든 정보는 DefaultSavedRequest 에 저장되고 이 객체는 HttpSessionRequestCache 에 의해 세션에 저장됩니다.

 

RequestCache와 savedRequest에 대해서 - 인프런 | 커뮤니티 질문&답변

누구나 함께하는 인프런 커뮤니티. 모르면 묻고, 해답을 찾아보세요....

www.inflearn.com

 

 

[사용자는 인증을 받은 상태, /user는 ADMIN ROLE을 가진 사용자만 접근 가능한 엔드포인트라고 가정한다.]

 

 

이때는 익명사용자도 아니고 , RememberMe[기억하기,자동로그인] 사용자가 아니므로 

 

AccessDeniedHandler가 호출되서 처리된다. 

 

 

 

 

 

댓글