티스토리 뷰

스프링 문서를 참고하여 정리하였습니다.

 

Servlet Authentication Architecture :: Spring Security

ProviderManager is the most commonly used implementation of AuthenticationManager. ProviderManager delegates to a List of AuthenticationProviders. Each AuthenticationProvider has an opportunity to indicate that authentication should be successful, fail, or

docs.spring.io

이 포스팅에서는 이전 포스팅에서 설명하는 서블릿 보안에 대해 확장하여 Servlet authentication에 사용되는 Spring Security의 주요 아키텍처 구성 요소를 다룬다.

 

스프링 시큐리티 [2]. Architecture

스프링 문서를 참고하여 정리하였습니다. Architecture :: Spring Security Spring Security’s Servlet support is based on Servlet Filters, so it is helpful to look at the role of Filters generally first..

kkoon9.tistory.com

SecurityContextHolder

Spring Security의 authentication 모델의 중심에는 SecurityContextHolder가 있다.

SecurityContextHolder는 SecurityContext를 포함한다.

SecurityContextHolder는 Spring Security가 인증 대상자에 대한 세부 정보를 저장하는 곳이다.

Spring Security는 SecurityContextHolder가 populate 되는 것에 대해서는 관심을 가지지 않는다.

값이 포함된 경우 현재 인증된 사용자로 사용된다.

사용자가 인증되었음을 나타내는 가장 간단한 방법은 SecurityContextHolder를 직접 설정하는 것이다.

// [1]
val context: SecurityContext = SecurityContextHolder.createEmptyContext() 
// [2]
val authentication: Authentication = TestingAuthenticationToken("username", "password", "ROLE_USER") 
context.authentication = authentication

// [3]
SecurityContextHolder.setContext(context)

[1] 설명

먼저 빈 SecurityContext를 만든다.

여러 스레드에서 race condition을 방지하려면 SecurityContextHolder.getContext()를 사용하는 대신 새 SecurityContext 인스턴스를 만드는 것이 중요하다.

[2] 설명

그런 다음, 새 Authentication 객체를 만든다.

Spring Security는 SecurityContext에 설정된 Authentication 구현체의 타입 역시 관심을 가지지 않는다.

여기서는 TestingAuthenticationToken를 사용한다.

더 일반적으로는 UsernamePasswordAuthenticationToken(userDetails, password, authorities)을 사용한다.

[3] 설명

마지막으로 SecurityContextHolder에 SecurityContext를 설정한다.

Spring Security는 이 정보를 authorization에 사용한다.


인증된 principal에 대한 정보를 가져오려면 SecurityContextHolder에 접근하면 된다.

val context = SecurityContextHolder.getContext()
val authentication = context.authentication
val username = authentication.name
val principal = authentication.principal
val authorities = authentication.authorities

기본적으로 SecurityContextHolder는 ThreadLocal을 사용하여 이러한 세부 정보를 저장한다.

즉, SecurityContext가 해당 메서드에 대한 인수로 명시적으로 전달되지 않더라도 동일한 스레드의 메서드에 SecurityContext를 항상 사용할 수 있다.

이러한 방식으로 ThreadLocal을 사용하는 것은 현재 주체의 요청이 처리된 후 스레드를 지우는 데 주의를 기울인다면 매우 안전하다.

Spring Security의 FilterChainProxy는 SecurityContext를 항상 지운다.

일부 application은 스레드와 함께 작동하는 특정 방식 때문에 ThreadLocal을 사용하는 데 완전히 적합하지 않다.

예를 들어, Swing 클라이언트는 Java Virtual Machine의 모든 스레드가 동일한 보안 컨텍스트를 사용하기를 원할 수 있다.

SecurityContextHolder는 시작할 때 컨텍스트 저장 방법을 지정하는 전략으로 구성할 수 있다.

standalone application의 경우, SecurityContextHolder.MODE_GLOBAL 전략을 사용한다.

다른 application은 보안 스레드에 의해 생성된 스레드를 동일한 보안 ID로 간주하도록 할 수 있다.

이 작업은 SecurityContextHolder.MODE_INHERITABLETHREADLOCAL를 사용하여 수행된다.

다음 두 가지 방법으로 기본 SecurityContextHolder.MODE_THREADLOCAL에서 모드를 변경할 수 있다.

  1. 시스템 속성을 설정
  2. SecurityContextHolder에서 정적 메서드를 호출

대부분의 application은 기본값에서 변경할 필요가 없지만, 변경할 경우 SecurityContextHolder용 Javadoc에서 자세히 알아보자.

SecurityContext

SecurityContext는 SecurityContextHolder에서 가져온다.

SecurityContext에 바로 아래에서 설명할 Authentication 객체가 있다.

Authentication

Authentication은 Spring Security 내에서 두 가지 주요 용도로 사용된다.

  • 사용자가 authentication에 제공한 credentials을 제공하기 위한 AuthenticationManager 입력 값으로 사용된다.
    • 이 시나리오에서 사용할 경우 isAuthenticated()는 false를 반환한다.
  • 인증된 사용자를 나타낸다.
    • Authentication은 SecurityContext에서 얻을 수 있다.

Authentication에는 다음이 포함된다.

  • principle
    • 사용자를 식별한다.
    • username/password로 인증하는 경우 이는 UserDetails의 인스턴스로 보면 된다.
  • credentials
    • 종종 password로 이용된다.
    • 대부분의 경우 사용자가 인증한 후 이 정보가 유출되지 않도록 지워진다.
  • authorities
    • GrantedAuthority은 사용자에게 부여된 높은 수준의 권한이다.
    • 예) roles, scopes

GrantedAuthority

GrantedAuthority은 Authentication.getAuthorities() 메서드에서 얻을 수 있다.

이 메서드는 GrantedAuthority 객체의 컬렉션을 제공한다.

GrantedAuthority는 당연히 principal에게 부여된 권한이다.

이러한 권한은 일반적으로 ROLE_ADMINISTRATOR 이나 ROLE_HR_SUPERVISOR과 같은 "roles" 이다.

이러한 역할은 나중에 웹 authorization, 메서드 authorization 및 도메인 객체 authorization에 대해 구성된다.

Spring Security의 다른 부분은 이러한 authority을 인터프리팅할 수 있다.

username/password authorization 사용 시, GrantedAuthority은 일반적으로 UserDetailsService에 의해 로드된다.

보통 GrantedAuthority 객체는 application 전체 권한이다.

특별한 도메인 객체와는 관련이 없다.

예를 들어, User 객체 id 54에 대한 authority을 나타낼 GrantedAuthority를 가지고 있지 않을 것이다.

왜냐하면 그러한 authority이 수천 개나 있다면 당신은 빠르게 메모리가 부족해질 것이다.

또는, 적어도, 애플리케이션이 사용자를 인증하는 데 오랜 시간이 걸릴 것이다.

물론, Spring Security는 이러한 일반적인 요구 사항을 처리하도록 특별히 설계되었지만, 프로젝트 내의 도메인 객체 보안 기능을 사용할 수 있다.

AuthenticationManager

AuthenticationManager는 Spring Security의 필터가 authentication을 수행하는 방법을 정의하는 API다.

반환되는 Authentication은 AuthenticationManager를 호출한 컨트롤러에 의해 SecurityContextHolder에서 설정된다.

Spring Security의 필터와 integration 하지 않는 경우 SecurityContextHolder를 직접 설정할 수 있으며 AuthenticationManager를 사용할 필요가 없다.

여러 AuthenticationManager의 구현체를 이용하는데, 가장 대표적인 구현체로 ProviderManager가 있다.

ProviderManager

위에서 언급했듯이 ProviderManager는 가장 일반적으로 사용되는 AuthenticationManager 구현체다.

ProviderManager는 여러 AuthenticationProvider에 delegate한다.

각 AuthenticationProvider는 authentication이 성공 혹은 실패를 나타내고, 하위 AuthenticationProvider가 결정을 내리도록 결정할 수 있는 역할을 가진다.

여러 AuthenticationProvider 중 어떠한 것도 인증할 수 없는 경우에는 ProviderNotFoundException으로 인증이 실패한다.

실제로, 각 AuthenticationProvider는 특정 유형의 authentication을 수행하는 방법을 알고 있다.

예를 들어, 한 AuthenticationProvider가 username/password를 검증할 수 있는 반면, 다른 AuthenticationProvider는 SAML assertion을 인증할 수 있다.

이렇게 하면 각 AuthenticationProvider가 특정 유형의 authentication을 수행하는 동시에, 여러 유형의 authentication을 지원하고 단일 AuthenticationManager bean만 노출할 수 있다.

또한, ProviderManager를 사용하면 AuthenticationProvider가 authentication을 수행할 수 없는 경우 참조되는 부모 AuthenticationManager를 구성할 수 있다.

부모는 모든 유형의 AuthenticationManager일 수 있지만, 종종 ProviderManager의 인스턴스다.

실제로 여러 ProviderManager 인스턴스가 동일한 상위 AuthenticationManager를 공유할 수 있다.

기본적으로 ProviderManager는 인증 요청에 의해 반환되는 Authentication 객체에서 중요한 credentials를 지우려고 시도한다.

이렇게 하면 암호와 같은 정보가 HttpSession에서 필요 이상으로 오래 보존되지 않는다.

예를 들어, stateless application의 성능을 향상시키기 위해 user 객체의 캐시를 사용할 때 문제가 발생할 수 있다.

UserDetails 인스턴스와 같은 캐시 내에 있는 객체에 대한 참조가 Authentication에 포함되어 있고, 해당 credentials이 제거된 경우에는 캐시된 값에 대해 더 이상 인증할 수 없다.

캐시를 사용하는 경우 이 점을 고려해야 한다.

해결책은 캐시 구현체 혹은 반환된 Authentication 객체를 생성하는 AuthenticationProvider에서 먼저 객체의 복사본을 만드는 것이다.

다른 방법으로는 ProviderManager 내의 프로퍼티인 eraseCredentialsAfterAuthentication를 사용하지 않도록 설정할 수 있다.

AuthenticationProvider

여러 AuthenticationProvider를 ProviderManager에 주입할 수 있다.

각 AuthenticationProvider는 특정 유형의 authentication을 수행한다.

예를 들어 DaoAuthenticationProvider는 username/password 기반 authentication을 지원하는 반면, JwtAuthenticationProvider는 JWT 토큰 authentication을 지원한다.

Request Credentials with AuthenticationEntryPoint

AuthenticationEntryPoint는 클라이언트로부터 credentials을 요청하는 HTTP response을 보내는 데 사용된다.

때때로 클라이언트는 리소스를 요청하기 위해 username/password와 같은 credentials을 사전에 포함한다.

credentials이 이미 포함되어 있기 때문에 Spring Security는 클라이언트의 credentials을 요청하는 HTTP response을 제공할 필요가 없다.

다른 경우 클라이언트는 액세스 권한이 없는 자원에 대해 인증되지 않은 요청을 한다.

이 경우 AuthenticationEntryPoint 구현체는 클라이언트에서 credentials을 요청하는 데 사용된다.

AuthenticationEntryPoint 구현체는 로그인 페이지로 리다이렉션하고, www-Authenticate 헤더로 응답하는 등의 작업을 수행할 수 있다.

AbstractAuthenticationProcessingFilter

AbstractAuthenticationProcessingFilter는 사용자의 credentials을 인증하기 위한 기본 필터로 사용된다.

일반적으로 credentials를 인증하기 전에 Spring Security는 AuthenticationEntryPoint를 사용하여 credentials를 요청한다.

AbstractAuthenticationProcessingFilter는 제출된 모든 인증 요청을 인증할 수 있다.

  1. 사용자가 credentials을 제출하면, AbstractAuthenticationProcessingFilter는 인증될 HttpServletRequest에서 Authentication을 만든다.
    1. 생성된 Authentication 타입은 AbstractAuthenticationProcessingFilter의 하위 클래스에 따라 다르다.
    2. 예를 들어, UsernamePasswordAuthenticationFilter가 HttpServletRequest에 제출된 username과 password를 통해 UsernamePasswordAuthenticationToken을 생성한다.
  2. Authentication이 AuthenticationManager로 전달되어 인증된다.
  3. 인증이 실패했을 땐 다음과 같다.
    1. 기존에 존재하던 SecurityContextHolder는 삭제된다.
    2. RememberMeServices.loginFail가 호출된다.
    3. AuthenticationFailureHandler가 호출된다.
  4. 인증이 성공했을 땐 다음과 같다.
    1. SessionAuthenticationStrategy에 로그인 사실이 전달된다.
    2. Authentication은 SecurityContextHolder에서 설정된다.
    3. RememberMeServices.loginSuccess가 호출된다.
    4. ApplicationEventPublisher가 InteractiveAuthenticationSuccessEvent를 퍼블리싱한다.
    5. AuthenticationSuccessHandler가 호출된다.
  1.  
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함