Spring/Spring Security

[Spring Security] 3. 인증: 아키텍쳐

noahkim_ 2021. 7. 28. 11:14

1. 인증 저장소 - SecurityContextHolder, SecurityContext

SecurityContextHolder

  • 인증된 유저의 정보를 보관하는 저장소입니다.
  • 다양한 방식의 인증 정보를 저장할 수 있습니다.
    • 저장되는 인증 정보의 타입만 맞으면 됩니다.
    • JWT, LDAP, OAuth, SSO 등 
  • 인증 정보를 저장하는 것이 사용자가 인증되었음을 나타내는 가장 간단한 방법입니다.
  • SecurityContextHolder안에 담긴 SecurityContext(인증 정보)를 어떻게 보관할지 전략을 설정할 수 있습니다.

 

SecurityContextHolderStrategy

  • SecurityContextHolder 내의 MODE_ 로 시작하는 static 변수로 선언되어 있습니다.

 

MODE_GLOBAL
  • 전체 애플리케이션에 SecurityContext가 오직 한개 입니다.

 

MODE_INHERITABLETHREADLOCAL
  • 현재 스레드의 SecurityContext를 자식스레드에 공유합니다.

 

(기본 옵션) MODE_THREADLOCAL
  • 스레드당 SecurityContext가 할당됩니다.
    • ThreadLocal에 SecurityContext가 저장됩니다.
  • 스레드와 생명주기가 같으므로 인증 처리를 한 후 인증 정보가 남지 않도록 비워주어야 합니다.
    • 비워주지 않으면, 스레드 재사용시에 이전 인증정보가 재사용될 수 있습니다.

 

SecurityContext

  • Authentication 객체가 저장되는 보관소로 필요 시 언제든지 Authentication 객체를 꺼내어 쓸 수 있도록 제공되는 인터페이스
  • 인증이 완료되면 HttpSession 에 저장되어 어플리케이션 전반에 걸쳐 접근이 가능합니다.

 

2. 인증저장소 필터 - SecurityContextHolderFilter

Persisting Authentication

  • 처음 유저가 보안 리소스에 접근을 요청할 때, 인증이 되어있지 않으므로 로그인 창으로 리다이렉트 합니다.
  • 유저가 리다이렉트된 로그인창을 통해 인증에 성공하면, 서버는 새로운 세션 아이디를 발급해줍니다.
  • 발급받은 세션 아이디 값을 통해 클라이언트는 서버와 인증이 지속될 수 있습니다.

 

SecurityContextRepository

  • 요청간의 SecurityContext를 지속하기 위해 사용되는 전략
  • DelegatingSecurityContextRepository 를 기본 구현체로 사용합니다.
    • DelegatingSecurityContextRepository는 여러 SecurityContextRepository 구현체들에게 위임하여 처리합니다.
  • SecurityContextRepository 구현체
    • HttpSessionSecurityContextRepository
      • SecurityContext를 HttpSession에 저장합니다 
      • 여러 요청에 걸쳐 SecurityContext를 유지할 수 있습니다. 
    • RequestAttributeSecurityContextRepository
      • SecurityContext를 ServletRequest의 attribute에 저장합니다
      • dispatch type이 발생할 때 SecurityContext를 복원할 수 있습니다.
      • 한 번의 요청에서만 SecurityContext를 유지할 수 있습니다.

 

SecurityContextHolderFilter

  • SecurityContextHolderFilter는 SecurityContextRepository로 SecurityContext를 로딩하는 역할을 맡은 필터입니다.
  1. SecurityContextHolderFilter는 SecurityContextRepository로 SecurityContext를 로딩하고
    SecurityContextHolder에 SecurityContext를 셋팅합니다
  2. SecurityContextHolderFilter는 로딩하는 역할만 하기 때문에 SecurityContext는 저장되어있는 상태여야 합니다.

 

3. 인증 객체 - Authentication

  • 인증된 유저의 정보를 표현하는 인터페이스 입니다.
  • Principal
    • user를 표현하는데 사용되는 필드입니다. (UserDetails 인터페이스)
  • Credentials
    • 식별정보에 대한 필드입니다.
    • 보통 password를 담으며, 유저가 인증되면 유출방지를 위해 지워집니다.
  • Authorities
    • 유저의 권한에 대한 필드입니다.
    • 역할과 범위로 표현됩니다 (GrantedAuthorities 인터페이스)
  • Authenticated
    • 인증성공 여부에 대한 필드입니다.
    • false일 경우, 만나는 모든 security interceptor는 인증절차를 요구합니다
      • 사용자가 보안 리소스 접근시도를 할 경우, authenticated 속성이 true인지 확인합니다
      • false일 경우, AuthorizationFIlter는 예외를 발생시킵니다
      • ExceptionTranslationFilter에 의해 사용자는 인증 절차로 리다이렉트 됩니다.

 

4. 인증 관리자 - AuthenticationManager

  • Spring Security의 필터가 어떻게 인증을 수행할 것인지 결정하는 API 객체입니다.
    • 인증 요청이 올 경우, 전달된 Authentication 객체를 대상으로 인증을 시도합니다.
  • 인증에 성공할 경우, 사용자의 권한정보가 들어있는 Authentication를 리턴해줍니다.
    • 리턴된 Authentication 객체는 SecurityContextHolder에 저장됩니다.
  • 인증에 실패할 경우, 예외가 발생합니다.
    • DisabledException : 계정이 enable 하지 않을 경우 (UserDetails 인터페이스의 isEnable() 리턴값 여부로 결정)
    • LockedException : 계정이 locked 되었을 경우 (UserDetails 인터페이스의 isAccountNonLocked() 리턴값 여부로 결정)
    • BadCredentialsException : Filter의 Provider가 인증 수행 도중 패스워드가 불일치 할 경우 (인증 수행 중, 예외를 발생시킴)
  • 인증 과정은 위의 예외 순으로 수행됩니다.
    • 불필요하게 패스워드 인증을 수행하지 않음으로써 논리적이고 효율적으로 동작합니다.

 

ProviderManager

  • ProviderManager는 AuthenticationManager의 대표적인 구현체입니다.

 

  • ProviderManager는 AuthenticationProvider가 담긴 List를 가집니다.
  • ProviderManager는 이 List에게 인증을 위임합니다.
  • List의 AuthenticationProvider들은 인증을 처리하면서
    • 성공할 경우, ProviderManager에게 사용자의 권한정보가 들어있는 Authentication를 리턴해줍니다
    • 실패할 경우, ProviderManager에게 AuthenticationException을 던집니다.
    • 자신이 지원하지 않는 Authentication 객체일 경우, 다음 순서의 AuthenticationProvider이 결정하도록 패스합니다.
  • List의 모든 AuthenticationProvider가 Authentication을 인증하는것을 지원하지 않는다면
    • ProviderNotFoundException를 던집니다.
  • List에게 위임하는 이러한 방식은
    • 특정 Authentication 객체별 인증을 처리하는 방식을 따로 처리하여 전문화할 수 있고 
    • 동시에 다양한 Authentication의 인증처리를 지원할 수 있습니다.

 

 

 

  • ProviderManager는 부모 AuthenticationManager를 가질 수 있습니다. (옵션)
    • ProviderManager의 List에서 해당 Authentication을 처리하지 못하는 경우, 의뢰하기 위해 사용됩니다.
  • ProviderManager들의 부모는 서로 같을 수 있습니다.
    • 보통 ProviderManager들의 인증처리가 같은 부분이 있는 경우, 부모에게 공통적으로 처리되는 인증을 맡깁니다.
    • 각 필터의 인증 방식만 다를경우, 다양한 인증 메커니즘을 구성하기 위해 필터의 ProviderManager를 다르게 구성합니다.

 

  • 기본적으로 ProviderManager는 인증 성공 후, 인증객체에 있는 민감정보를 지웁니다.
    • 보안을 강화하기 위함입니다.
    • HttpSession에 민감정보가 필요이상으로 남아있는 것을 방지합니다.
  • 그러나 이 정보가 지워지면 캐싱해서 사용하지 못합니다.
  • 인증객체 정보를 캐싱하는 방법이 있습니다.
    • AuthenticationProvider에 캐싱기능을 구현한 구현체를 사용합니다.
    • ProviderManager의 eraseCredentialsAfterAuthentication 속성을 false로 설정합니다.

 

5. 인증 처리자 - AuthenticationProvider

  • 인증 처리를 위해 ProviderManager에서 쓸 AuthenticationProvider를 셋팅할 수 있습니다.
  • 각 AuthenticationProvider는 특정 타입의 Authentication을 처리할 수 있습니다.

 

6. 인증 요청 담당 - AuthenticationEntryPoint

  • AuthenticationEntryPoint는 클라이언트에게 인증 요청에 대한 응답을 보내는데 쓰여집니다.
  • 보안 리소스에 요청할 경우 인증이 필요합니다.
    • 클라이언트가 이미 인증되어 있다면, 바로 접근이 가능합니다.
    • 클라이언트가 이미 인증되어 있지 않다면, 서버는 클라이언트에게 보안 리소스를 제공할 수 없습니다.
      • 클라이언트는 서버로부터 인증되어야 제공받을 수 있습니다. 
      • 서버는 클라이언트에게 인증을 요청해야 합니다.
      • 이때 AuthenticationEntryPoint가 동작하여 클라이언트에게 인증요청 응답을 내려줍니다
  • AuthenticationEntryPoint는 다양한 응답을 가집니다.
    • 보통 로그인 페이지 리다이렉션
    • WWW-Authenticate 헤더 응답
    • 커스터마이징된 동작

 

7. 인증 필터 - AbstractAuthenticationProcessingFilter

  • AbstractAuthenticationProcessingFilter는 비밀번호 기반 인증을 처리하는 필터로 사용됩니다.
  • AbstractAuthenticationProcessingFilter는 사용할 AuthenticationManager를 필드에 셋팅하는 것이 필수입니다.
    • AbstractAuthenticationProcessingFilter의 AuthenticationManager는 주로 인증 요청 토큰을 처리하는데 씁니다.
    • 인증 요청 토큰은 특정 구현 클래스들에 의해 생성됩니다.
  • AbstractAuthenticationProcessingFilter는 요청을 가로챕니다.
    • 요청을 가로채어 자신이 처리할 수 있는 인증 객체인지 판단한 후 인증을 진행합니다.
  • AbstractAuthenticationProcessingFilter는 추상클래스이므로 구현 매커니즘 별 구현된 서브클래스가 인증을 수행합니다.

 

  • 클라이언트가 민감정보를 제출하여 인증 시도를 요청할 때
    • AbstractAuthenticationProcessingFilter는 HttpServletRequest로부터 Authentication을 생성합니다.
    • 생성된 Authentication는 AbstractAuthenticationProcessingFilter의 서브클래스에 따라 달라집니다.
    • ex) UsernamePasswordAuthenticationFilter -> UsernamePasswordAuthenticationToken
  • AuthenticationManager는 전달된 Authentication를 가지고 인증을 시도합니다.
  • 인증이 실패하였다면
    • SecurityContextHolder는 비워집니다.
    • RememberMeService의 loginFail()이 호출됩니다. (rememberme가 설정되어 있는 경우에만 호출됨)
    • AuthenticationFailureHandler가 호출됩니다.
  • 인증이 성공하였다면
    • SessionAuthenticationStrategy가 새로운 로그인을 알게됩니다. (세션 공격 정책에 의한 동작이 수행될 수 있음)
    • SecurityContextHolder에 Authentication이 담겨집니다.
    • RememberMeService의 loginSuccess()이 호출됩니다. (rememberme가 설정되어 있는 경우에만 호출됨)
    • ApplicationEventPublisher가 InteractiveAuthenticationSuccessEvent 를 발행합니다.
    • AuthenticationSuccessHandler가 호출됩니다.

 

 

 

참고