이 영역을 누르면 첫 페이지로 이동
천천히 꾸준히 조용히 블로그의 첫 페이지로 이동

천천히 꾸준히 조용히

페이지 맨 위로 올라가기

천천히 꾸준히 조용히

천천히 꾸준히 조용히.. i3months 블로그

[Spring Security] 예외 처리

  • 2024.06.12 20:16
  • Spring/Spring Security
반응형

 

 

 

스프링 시큐리티에서 발생하는 예외는 AuthenticationException과 AccessDeniedException으로 나뉘며, ExceptionTranslationFilter가 예외를 처리한다.

 

AuthenticationException 발생 시 SecurityContext 에서 인증 정보를 삭제하고 Authentication을 초기화한다.

 

필터는 authenticationEntryPoint를 실행해 인증 실패를 공통적으로 처리하고 인증을 요청할 수 있는 화면으로 이동시킨다.

인증 프로세스의 요청 정보는 RequestCache, SavedRequest에 저장해 사용자가 인증을 마친 후 재사용한다.

 

AccessDeniedException 발생 시 필터는 사용자가 익명 사용자인지를 먼저 판단하고, 익명 사용자면 인증 예외 처리를 실행하고 익명 사용자가 아닌 경우 AccessDeniedHandler에게 처리를 위임한다. 

 

public interface AuthenticationEntryPoint {
	void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
			throws IOException, ServletException;
}

public ExceptionHandlingConfigurer<H> authenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
    this.authenticationEntryPoint = authenticationEntryPoint;
    return this;
}

// SecurityConfig.java
http.exceptionHandling(exception -> exception
    .authenticationEntryPoint((request, response, authException) -> {
        // ..
    })
    .accessDeniedHandler((request, response, accessDeniedException) -> {
        //...
    })

);

 

 

이렇게 직접 EntryPoint와 DeniedHandler를 설정하면 직접 설정한 클래스가 가장 먼저 적용되고, 설정하지 않으면 각 인증 프로세스마다 기본적으로 제공되는 클래스들이 설정된다. 

 

직접 설정하는 경우 기본 로그인 페이지 생성 작업이 무시된다.

 

 

 

 

 

 

사용자의 요청이 인증이 필요한 요청이지만 인증되지 않은 상태인 경우 AuthorizationFilter는 인가 예외를 뱉고, ExceptionTranslationFilter가 처리한다.

 

인가 예외는 AccessDeniedException 이지만, 익명 사용자 관련 예외이기에 인증 예외인 AuthenticationException으로 분류된다. 

 

이후 SecurityContext를 비우고 HttpSessionRequestCache에 사용자의 요청을 저장한 후 AuthenticationEntryPoint를 호출해 공통된 작업을 처리한다. 

 

AuthorizationFilter에서 발생한 예외는 ExceptionTranslationFilter가 처리하게 되고, AuthorizationFilter에서는 인가 예외만 던지고 해당 사용자의 인증 상태를 확인하고 인가 예외로 처리할지 인증 예외로 처리할지는 ExceptionTranslationFilter가 결정한다. 

 

 

// AuthorizationFilter.java
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
        throws ServletException, IOException {

    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;

    if (this.observeOncePerRequest && isApplied(request)) {
        chain.doFilter(request, response);
        return;
    }

    if (skipDispatch(request)) {
        chain.doFilter(request, response);
        return;
    }

    String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
    request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
    try {
        AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request);
        this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision);
        if (decision != null && !decision.isGranted()) {
            throw new AccessDeniedException("Access Denied");
        }
        chain.doFilter(request, response);
    }
    finally {
        request.removeAttribute(alreadyFilteredAttributeName);
    }
}

// ExceptionTranslationFilter.java
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    try {
        chain.doFilter(request, response);
    }
    catch (IOException ex) {
        throw ex;
    }
    catch (Exception ex) {
        // Try to extract a SpringSecurityException from the stacktrace
        Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
        RuntimeException securityException = (AuthenticationException) this.throwableAnalyzer
            .getFirstThrowableOfType(AuthenticationException.class, causeChain);
        if (securityException == null) {
            securityException = (AccessDeniedException) this.throwableAnalyzer
                .getFirstThrowableOfType(AccessDeniedException.class, causeChain);
        }
        if (securityException == null) {
            rethrow(ex);
        }
        if (response.isCommitted()) {
            throw new ServletException("Unable to handle the Spring Security Exception "
                    + "because the response is already committed.", ex);
        }
        handleSpringSecurityException(request, response, chain, securityException);
    }
}

 

 

AccessDeniedException을 뱉으면 ExceptionTranslationFilter가 받아서 처리한다.

넘어온 예외가 AccessDeniedException인지를 확인하고 인가 예외인 경우 익명 사용자 여부를 체크한다.

 

 

스프링 시큐리티가 제공하는 exceptionHandling() api 는 인증 / 인가 관련 예외를 처리할 때 사용된다.

 

authenticationEntryPoint / accessDeniedHandler / accessDeinedPage 메서드로 인증되지 않은 사용자와 권한이 없는 사용자를 처리한다.

 

스프링 시큐리티에서는 401, 403 번 에러만 처리할 수 있으니.. 다른 에러도 처리해야 하는 경우 필터를 만들어서 처리하자. 

반응형
저작자표시 (새창열림)

'Spring > Spring Security' 카테고리의 다른 글

[Spring Security] 인가 프로세스  (0) 2024.06.22
[Spring Security] 인증 상태 영속성  (0) 2024.06.10
[Spring Security] 인증 아키텍처  (0) 2024.06.08
[Spring Security] 인증 메커니즘  (0) 2024.05.30
[Spring Security] 초기화 과정  (0) 2024.05.27

댓글

이 글 공유하기

  • 구독하기

    구독하기

  • 카카오톡

    카카오톡

  • 라인

    라인

  • 트위터

    트위터

  • Facebook

    Facebook

  • 카카오스토리

    카카오스토리

  • 밴드

    밴드

  • 네이버 블로그

    네이버 블로그

  • Pocket

    Pocket

  • Evernote

    Evernote

다른 글

  • [Spring Security] 인가 프로세스

    [Spring Security] 인가 프로세스

    2024.06.22
  • [Spring Security] 인증 상태 영속성

    [Spring Security] 인증 상태 영속성

    2024.06.10
  • [Spring Security] 인증 아키텍처

    [Spring Security] 인증 아키텍처

    2024.06.08
  • [Spring Security] 인증 메커니즘

    [Spring Security] 인증 메커니즘

    2024.05.30
다른 글 더 둘러보기

정보

천천히 꾸준히 조용히 블로그의 첫 페이지로 이동

천천히 꾸준히 조용히

  • 천천히 꾸준히 조용히의 첫 페이지로 이동

검색

방문자

  • 전체 방문자
  • 오늘
  • 어제

카테고리

  • 분류 전체보기 (677)
    • Algorithm (205)
      • Data Structure (5)
      • Theory && Tip (33)
      • Baekjoon (166)
      • ALGOSPOT (1)
    • Spring (123)
      • Spring (28)
      • Spring Web MVC (20)
      • Spring Database (14)
      • Spring Boot (6)
      • Spring 3.1 (11)
      • Spring Batch (6)
      • Spring Security (16)
      • JPA (12)
      • Spring Data JPA (5)
      • QueryDSL (4)
      • eGovFramework (1)
    • Programming Language (74)
      • C (25)
      • C++ (12)
      • Java (19)
      • JavaScript (15)
      • Python (1)
      • PHP (2)
    • Computer Science (142)
      • Machine Learning (38)
      • Operating System (18)
      • Computer Network (28)
      • System Programming (22)
      • Universial Programming Lang.. (8)
      • Computer Architecture (4)
      • Compiler Design (11)
      • Computer Security (13)
    • Database (21)
      • Database (7)
      • MySQL (3)
      • Oracle (3)
      • Redis (5)
      • Elasticsearch (3)
    • DevOps (20)
      • Docker && Kubernetes (8)
      • Jenkins (4)
      • Amazon Web Service (8)
    • Mobile (28)
      • Android (21)
      • Flutter (7)
    • 💡 솔루션 (17)
    • 👥 모각코 (9)
    • 💬 기록 (7)
    • 📚 공부 (6)
    • -------------- (25)

최근 글

나의 외부 링크

메뉴

  • 홈
반응형

정보

i3months의 천천히 꾸준히 조용히

천천히 꾸준히 조용히

i3months

블로그 구독하기

  • 구독하기
  • RSS 피드

티스토리

  • 티스토리 홈
  • 이 블로그 관리하기
  • 글쓰기
Powered by Tistory / Kakao. Copyright © i3months.

티스토리툴바