Spring

[Spring Security] 스프링 시큐리티 인증 아키텍처 컴포넌트

림 림 2020. 12. 27. 14:25
반응형

스프링 시큐리티에서 제공하는 기능 ⭐️⭐️⭐️

스프링 시큐리티는 사용자를 인증하고, 권한을 부여하는 기능을 제공합니다. 또, 여러 공격에 대해 보호하는 기능을 제공합니다.

그 중에 인증과 권한은 스프링 시큐리티에서 가장 중요한 보안 개념입니다.

 

인증(Authentication)

인증은 '누구'인지를 검증하는 과정입니다. 
사용자가 특정 자원에 접근하기 위해서는 그럴 권한이 있는 사용자라는 것을 증명해야합니다.
따라서 사용자는 사용자를 증명하기 위한 정보(credentials)를 제공하고, 인증은 바로 그 정보를 검증하는 것을 말합니다.

사용자를 인증하는 흔한 방법 중에 하나는 사용자에게 이름(username)과 비밀번호(password)를 입력받는 것입니다.
인증이 완료되면 스프링 시큐리티는 사용자가 '누구'인지 확인할 수 있게되고, 그 사용자의 권한에 따라 자원을 제공하고 보호하는 기능을 제공합니다.

스프링 시큐리티는 아키텍처 컴포넌트 인증 매커니즘을 통해 인증 기능을 제공합니다.
아키텍처 컴포넌트에는 다음과 같은 것들이 있습니다.

- SecurityContextHolder: 인증된 사용자에 대한 상세 정보를 저장하는 공간. 
- SecurityContext: 현재 인증된 사용자에 대한 정보. SecuritycontextHolder에 저장되며 Authentication을 담고 있습니다.
- Authentication: 현재 인증된 사용자의 고유 아이덴티티(principal), 자격 증명 정보(credentials), 역할 정보(authorities).
- GrantedAuthority: Authentication에 포함되는 사용자의 역할 정보.
- AuthenticationManager스프링 시큐리티의 필터들이 인증을 수행하기 위해 이용하는 API. 시큐리티 필터로부터 Authentication을 입력받음.
- ProviderManager: AuthenticationManager의 구현체. 여러 AuthenticationProvider의 그룹.
- AuthenticationProvider: 각각 하나의 타입의 인증을 맡아서 인증을 수행.
- Request Credentials with AuthenticationEntryPoint: 사용자에게 자격 증명 정보를 요청.
- AbstractAuthenticationProcessingFilter: 사용자로부터 받은 자격 증명 정보를 인증하는 기본 필터.

각각의 아키텍처 컴포넌트에 대해 살펴보겠습니다.

 

[SecurityContextHolder]

SecurityContextHolder는 스프링 시큐리티가 인증된 사용자에 대한 상세 정보를 저장하는 공간입니다.
즉, 현재 SecurityContextHolder가 어떤 값을 포함하고 있다면 두 가지를 의미한다고 볼 수 있습니다.
바로 현재 인증된 사용자가 있다는 것과 SecurityContextHolder가 포함하고 있는 값이 그 사용자에 대한 정보라는 것입니다.

SecurityContextHolder 구조

SecurityContextHolder의 구조는 위의 사진과 같습니다.
SecurityContextHolder는 SecurityContext를 담고 있고, SecurityContext는 Authentication을 담고 있습니다.
그리고 Authentication은 Principal, Credentials, Authorities를 담고 있습니다.

SecurityContext context = SecurityContextHolder.createEmptyContext(); 
Authentication authentication =
    new TestingAuthenticationToken("username", "password", "ROLE_USER"); 
context.setAuthentication(authentication);

SecurityContextHolder.setContext(context); 

코드를 보면 SecurityContextHolder의 createEmptyContext()를 통해 SecurityContext 객체를 초기화할 수 있습니다.
그리고 SecurityContext의 setAuthentication()를 통해 Principal, Credentials, Authorities 값을 넣은 Authentication 객체를 초기화할 수 있습니다.
그리고 나서 SecurityContextHolder의 setContext()를 통해 SecurityContext를 SecurityContextHolder에 저장함으로써 현재 인증된 사용자에 대한 정보를 저장할 수 있습니다.
현재 인증된 사용자에 대한 정보를 얻으려면 SecurityContextHolder의 getContext()를 이용하면 됩니다.

 

[SecurityContext]

SecurityContext는 현재 인증된 사용자에 대한 정보입니다.
SecurityContextHolder를 통해 얻을 수 있고, Authentication을 담는 객체입니다.

 

[Authentication]

Authentication은 스프링 시큐리티의 두 가지 목적으로 사용됩니다.

1) 인증을 위해 사용자의 정보를 제공하기 위해 사용됩니다. 인증을 수행하기 위한 API인 AuthenticationManager의 입력으로 전달됩니다.
2) 현재 인증된 사용자를 나타내는 데에 사용됩니다. SecurityContextHolder로부터 SecurityContext를 얻고, SecurityContext로부터 Authentication을 얻어 현재 인증된 사용자를 나타낼 수 있습니다.

Authentication은 세 가지를 포함하고 있습니다.

1) principal: 고유의 사용자를 나타냅니다. username/password로 인증을 할 경우, principal은 UserDetails 객체가 됩니다.
2) credentials: 자격 증명을 나타냅니다. 예를 들면 password가 될 수 있습니다.
3) authorities: 역할이나 접근 범위를 나타내는 GrantAuthority 객체입니다.

 

[GrantedAuthority]

역할(role)이나 범위(scopes)를 나타내는 객체입니다.

GrantedAuthority는 Authentication 객체에 포함된 authorities입니다. 
Authentication.getAuthorities()를 통해 얻을 수 있는데, 이 메소드는 GrantedAuthority의 컬렉션을 반환합니다.

고수준의 의미상으로 생각하면 권한이라고 생각할 수도 있지만, GrantedAuthority는 사용자(principal)에게 허가된 권한이 아니라 ROLE_ADMIN, ROLE_HR_SUPERVISER과 같은 역할 정보입니다.

 

[AuthenticationManager]

스프링 시큐리티의 필터들이 인증을 수행하기 위해 이용하는 API입니다.
AuthenticaionManager의 주 구현체는 ProviderManager입니다.

 

[ProviderManager & AuthenticationProvider]

ProviderManager는 가장 자주 쓰이는 AuthenticationManager의 구현체입니다. 
ProviderManager는 여러 개의 AuthenticationProvider들을 포함합니다. 

각각의 AuthenticationProvider는 하나의 타입의 인증을 맡아서 수행합니다.
예를 들어, DaoAuthenticationProviders는 username/password 기반의 인증을 수행하고, JwtAuthenticationProvider는 JWT토큰을 인증합니다.

AuthenticationProvider는 인증의 성공/실패의 결과를 제공합니다. 만약 성공/실패의 결정을 내릴 수 없다면, 결정을 내릴 수 없음을 나타내고 다른 AuthenticationProvider에게 결정을 위임합니다.

ProviderManager

만약 마지막 AuthenticationProvider까지 내려갔는데 인증을 할 수 없으면, ProviderNotFoundException 예외가 발생하고 인증에 실패하게 됩니다.

ProviderManager는 선택적으로 참조할 수 있는 상위 AuthenticationManager를 구성할 수 있습니다. 
만약 모든 AuthenticationProvider들이 인증을 수행할 수 없으면, 상위의 AuthenticationManager를 참조하게 됩니다.

ProviderManager-parent

 

여러 ProvideeManager가 하나의 AuthenticationManager를 공유하도록 구성함으로써 여러 개의 인증 매커니즘을 여러 ProviderManager를 통해 수행하고, 공통적으로 수행하는 인증은 AuthenticationManager에서 수행하게 할 수도 있습니다.

ProviderManagers-parent

 

[Request Credentials with AuthenticationEntryPoint]

AuthenticationEntryPoint는 클라이언트에게 자격 증명 정보(credentials)를 요청하는 HTTP 응답을 보내는 데에 사용됩니다.

사용자가 미리 로그인을 하는 경우에는 스프링 시큐리티가 사용자에게 자격 증명 정보를 요청하는 HTTP 응답을 보낼 필요가 없습니다.
그런데 만약, 인증되지 않은 사용자가 어떤 자원에 대해 요청을 보내면, AuthenticationEntryPoint의 구현체를 통해 클라이언트에게 자격 증명 정보를 요청합니다.

AuthenticationEntryPoint는 로그인 페이지로 리다이렉트하거나, 클라이언트에게 401 응답 코드와 함께 WWW-Authenticate 헤더를 보내는 작업을 수행합니다.

 

[AbstractAuthenticationProcessingFilter]

AbstractAuthenticationProcessingFilter는 사용자의 자격 증명 정보를 인증하는 기본 필터로 사용됩니다.

스프링 시큐리티가 AuthenticationEntryPoint로 자격 증명 정보를 요청하고 나면, AbstractAuthenticationProcessingFilter가 인증 요청을 수행합니다.

 

AbstractAuthenticationProcessingFilter

1) 사용자가 자격 증명 정보를 제출하면, AbstractAuthenticationProcessingFilter가 Authentication객체를 생성합니다.
Authentication객체는 AbstractAuthenticationProcessingFilter의 서브클래스에 의해 생성되고, 그 타입이 결정됩니다.
예를 들어, UsernamePasswordAuthenticationFilter는 사용자로부터 받은 username과 password로부터 UsernamePasswordAuthenticationToken을 생성합니다.

2) Authentication객체가 AuthenticationManager에게 전달됩니다.

3) 만약 인증에 실패하면, SecurityContextHolder의 값이 지워지고, RememberMeService.joginFail()이 실행됩니다.
그리고 AuthenticationFailureHandler가 실행됩니다.

4) 만약 인증에 성공하면, SessionAuthenticationStrategy가 새로운 로그인이 되었음을 알리고, Authentication이 SecurityContextHolder에 저장됩니다. 이후에 SecurityContextPersistenceFIlterrk SecurityContext를 HttpSession에 저장하면서 로그인 세션 정보가 저장됩니다.
그리고 RememberMeServices.loginSucess()가 실행됩니다. 또, ApplicationEventPublisher가 InteractiveAuthenticationSuccessEvent를 발생시키고 AuthenticationSuccessHandler가 실행됩니다.

반응형