[데브코스] Spring Security - 1

Updated: Categories:

W15D2 - Spring Security 기본구조 / FilterChainProxy에 대해 알아보자.

Spring Security Architecture

기본 구조

image

  • Spring Security는 웹에서의 요청을 받으면 가장 먼저!! 인증과 인가를 수행한다.
    • 그 이후 Servlet에 전달하게됨.
  • AuthenticationManager : 사용자 인증 관련 처리
  • AccessDecisionManager : 사용자가 보호받는 리소스에 접근할 수 있는 적절한 권한이 있는지 확인

FilterChainProxy

  • Spring Security는 서블릿 필터 javax.servlet.Filter의 구현체로 동작한다.
  • @EnableWebSecurity 어노테이션과 함께 WebSecurityConfigurerAdapter 추상 클래스를 상속받아 구현함
    • 웹 요청은 이러한 필터 체인을 차례로 통과하게 됨
      • 웹 요청은 모든 필터를 통과하게 되지만, 모든 필터가 동작하는 것은 아님
      • 각 필터는 웹 요청에 따라 동작 여부를 결정할 수 있고, 동작할 필요가 없다면 다음 필터로 웹 요청을 즉시 넘김
    • 요청을 처리하고 응답을 반환하면 필터 체인 호출 스택은 모든 필터에 대해 역순으로 진행
    • 보통 springSecurityFilterChain 이라는 이름으로 Bean 등록됨

image

  • Spring Security는 Filter를 잘 이해하고 사용할 수 있어야 함!

필터 종류

  • 필터 흐름에 따라 순서가 있다. 주요필터들을 정리해보자면..
필터 설명
ChannelProcessingFilter 웹 요청이 어떤 프로토콜로 (http 또는 https) 전달되어야 하는지 처리
SecurityContextPersistenceFilter SecurityContextRepository를 통해 SecurityContext를 Load/Save 처리
LogoutFilter 로그아웃 URL로 요청을 감시하여 매칭되는 요청이 있으면 해당 사용자를 로그아웃 시킴
UsernamePasswordAuthenticationFilter ID/비밀번호 기반 Form 인증 요청 URL(기본값: /login) 을 감시하여 사용자를 인증함
DefaultLoginPageGeneratingFilter 로그인을 수행하는데 필요한 HTML을 생성함
RequestCacheAwareFilter 로그인 성공 이후 인증 요청에 의해 가로채어진 사용자의 원래 요청으로 이동하기 위해 사용됨
SecurityContextHolderAwareRequestFilter 서블릿 3 API 지원을 위해 HttpServletRequest를 HttpServletRequestWrapper 하위 클래스로 감쌈
RememberMeAuthenticationFilter 요청의 일부로 remeber-me 쿠키 제공 여부를 확인하고, 쿠키가 있으면 사용자 인증을 시도함
AnonymousAuthenticationFilter 해당 인증 필터에 도달할때까지 사용자가 아직 인증되지 않았다면, 익명 사용자로 처리하도록 함
ExceptionTranslationFilter 요청을 처리하는 도중 발생할 수 있는 예외에 대한 라우팅과 위임을 처리함
FilterSecurityInterceptor 접근 권한 확인을 위해 요청을 AccessDecisionManager로 위임


ChannelProcessingFilter

  • ChannelProcessingFilter 설정을 통해 HTTPS 채널을 통해 처리해야 하는 웹 요청을 정의할 수 있음
  • 실제적인 처리를 ChannelDecisionManager 클래스로 위임함


AnonymousAuthenticationFilter

  • 비교적 필터체인 마지막에 동작하는 필터
  • 해당 필터에 요청이 도달할때까지 사용자가 인증되지 않았다면, 사용자를 null 대신 Anonymous 인증 타입으로 표현
    • name = anonymousUser으로 처리한다
AnonymousAuthenticationFilter class

image

  • 인증이 null이면 default값으로 정보를 넣어주는것을 확인할 수 있음
  • 아래처럼 이름과 권한을 커스텀할 수 있다.
http.anonymous()
    .principal(익명유저 이름 설정)
    .authorities(익명유저 권한 설정)


ExceptionTranslationFilter

  • FilterSecurityInterceptor 바로 위에 위치하며, FilterSecurityInterceptor 실행 중 발생할 수 있는 예외를 잡고 처리함
  • ExceptionTranslationFilter는 필터체인 실행스택에서 자기 아래에 오는 필터들에서 발생하는 예외들에 대해서만 처리할 수 있다. (따라서 커스텀 필터를 추가해야 하는 경우 커스텀 필터를 적당한 위치에 두어야 한다.)

image

  • AuthenticationException : 인증 관련 예외이며, 사용자를 로그인 페이지로 보냄
  • AccessDeniedException : 권한 관련 예외, AccessDecisionManager에 의해 접근 거부가 발생했을 때 접근 거부 페이지를 보여주거나 사용자를 로그인 페이지로 보냄
  • AuthenticationEntryPoint : 사용자를 정해진 페이지로 포워딩해주는 역할

커스텀

  • AccessDeniedHandler를 리턴하는 메소드를 Bean으로 띄워 커스텀이 가능하다
AccessDeniedHandler 작성
@Bean
public AccessDeniedHandler customAccessDeniedHandler() {
    return (request, response, e) -> {
        ...
    };
}
exceptionHandling 필터 작성
http.exceptionHandling()
    .accessDeniedHandler(customAccessDeniedHandler())


RequestCacheAwareFilter

  • 로그인 성공 이후 인증 요청에 의해 가로채어진 사용자의 원래 요청으로 이동하기 위해 사용되는 필터
  • 이게 무슨 말일까? 예시를 통해 이해해보자.

예시

  • 어떤 익명 사용자가 권한이 필요한 페이지에 접근하려 한다면? 아래와 같은 일이 일어난다.

img

  1. 페이지에 접근 권한이 없으므로 ExceptionTranslationFilterAccessDenied에 걸리게 된다.
  2. RequestCacheAwareFilter에 의해 해당 페이지 요청은 캐시에 저장된다.
  3. 마지막으로 default 설정인 /login으로 리다이렉트 시킴.
  • 그리고 사용자가 리다이렉트된 페이지에서 로그인한다면?
    • 로그인 후 default 설정은 루트 url /로 이동시키는 것이지만, 맨 처음 요청했던 페이지로 이동된다.
    • 이를 가능하게 하는것이 RequestCacheAwareFilter의 기능!

img