카테고리 없음

[Spring] Spring Security 기본 개념-1. Authentication, Authentication Manager & Provider (+User, UsernamePasswordToken)

빠작 2023. 8. 12. 19:38

JWT 로그인을 구현하면서 너무 많은 개념이 등장해 이해하기 힘들었다.

그래서 Spring 공식 문서를 읽고 이해한 내용을 정리해보았다.

 

우선 스프링 시큐리티에서 인증이 일어나는 전체 과정을 한 번 살펴보자.


우선 1단계를 살펴보자.

 

Authentication은 1번 과정에서 필요한 개념이다. 즉, 모든 인증과정의 첫 단계를 밟기 위해 제출해야 하는 통행료같은 개념이라고 생각하면 쉽다. 이는 Principal, Credentials, Authorities로 이루어져 있다.

Authentication

Authentication의 구성 요소

Principal에는 사용자에 대한 정보가 담긴다. 그런데 서비스 측에서 사용자에 대해 추가적으로 기록해둔 정보가 아닌, 사용자가 가입할 때 제출했던 정보이다.

Credential에는 주로 사용자가 지정한 비밀번호가 담긴다.

Authorities에는 사용자의 권한이 담긴다. 주로 ROLE_USER, ROLE_ADMIN등이 그 예시이다.

 

Authentication은 인증 과정의 1단계이다.

 

이 Authentication 객체는 필터에서 생성된다.

필터는 사용자 정보를 바탕으로 Authentication 객체를 만들고, 이를 AuthenticationManager에게 넘긴다.

 


이제 2단계를 살펴보자.

AuthenticationManager은 2번 과정에서 보이듯이 1단계에서 들어온 Authentication 객체를 정말 인증해줄지, 말지를 결정해주는 API이다. 여기서 오케이! 인증! 해주면 이 Authentication 객체가 SecurityContext에도 저장되는거고... 아님 핸들러 작동하는거고... 이를 상속해서 많이들 사용하는데 가장 대표적인게 바로 ProviderManager이다.

 

여기서 한 가지 의문이 든다.

왜 AuthenticationManager가 API인가? 이 안에서 어떤 일이 일어나길래?

이름에 Manager가 붙은 것만 봐도 의심스럽지 않은가?

AuthenticationManager 안에는 수많은 AuthenticationProvider들이 존재한다.

ProviderManager가 워낙 유명하니 이 글에서는 얘나 AuthenticationManager나 같은 뜻으로 부르겠다.

 

인증은 AuthenticationManagerauthenticate메서드를 통해 진행된다.

인풋으로 들어온 Authentication 타입 객체는 manager 안의 수많은 AuthenticationProvider들을 거치는데, 어느 단계에서 인증에 성공하면 인증된 Authentication 객체가 리턴되는거고, 아니면 다음번 provider에게로 계속해서 넘겨지는거다.

인증에 성공하면 manager가 리턴한 Authentication 객체를 SecurityContextHolder에 이를 저장해주면 된다.

모든 Provider들을 거쳤는데도 인증이 되지 않는다면 ProviderNotFoundException이 발생한다.

 

즉, 어떤 조건에 대해 이 Authentication 객체가 인증 가능한가를 따지는 것이 AuthenticationProvider이고,

AuthenticationProvider의 집합을 가지고 이들을 호출하는 것이 AuthenticationManager이다.

그리고 ProviderManagerAuthenticationManager의 가장 일반적인 구현체이다.

 

 

여기서 또 하나의 의문이 들텐데

Authentication은 인증의 1단계라고 했는데? AuthenticationManager의 인풋인데 또 리턴되는 값이기도 한다고?

그 이유는 공식 문서의 Authentication의 정의를 살펴보면 알 수 있다. 사실 Authentication은 2가지 상태를 나타낸다. 

AuthenticationManager의 인풋으로서 사용자가 인증받기 위해 제공한 credentials를 제공하거나,

SecurityContext에 저장된 현재의 사용자를 나타낸다.

그래서 manager의 인풋이자 아웃풋이 되는 것이다.

Authentication의 뜻

 


그럼 이제 UserUsernamePasswordToken에 대해 알아보자.

이전에 SecurityFilterChain에 기본적으로 존재하는 Authentication 관련 필터들에 대해 알아야 한다. 필터체인에 존재하는 모든 필터가 Authentication과 관련된 것은 아니기에 관련 필터는 몇개만 존재하는데 이 중 가장 앞에 등장하고, 가장 많이 사용되는 것이 바로 UsernamePasswordAuthentication filter이다.

 

필터가 생성하는 Authentication 객체가 바로 UsernamePasswordToken이다.

필터는 이 토큰을 생성하여 AuthenticationManager에게 넘기는데, 그러면 DAOAuthenticationProvider에서 DB에 있는 사용자 정보를 가져와 이 토큰 속 정보와 비교한다. 이 provider가 DB에서 가져온 정보를 저장하는 객체가 바로 User이다. user는 UserDetails 타입 중 하나로, username과 password만 저장하는 기본형이다. 만약 더 많은 정보를 저장하고 싶다면 UserDetails를 직접 커스텀하면 된다.


여기까지만 알아도 JWT 로그인을 구현하는데 무리는 없었다.

아래 내용은 공식 문서에 등장하지만 나도 사용해본 적은 없어 정확하게 알지 못하는 내용이다.


 

ProviderManager가 보유하고 있는 AuthenticationProvider 중 어느 것도 Authentication 객체를 인증해주지 못하는 경우에 대비하여 parent로서 AuthenticationManager을 가질 수 있다고 한다. 

parent는 AuthenticationManager 중 어느 타입도 가능하지만, ProviderManager가 가장 일반적이라고 한다.

 

 

여러개의 ProviderManager가 같은 parent AuthenticationManager을 갖는 경우도 있는데,

여러개의 SecurityFilterChain이 존재하고,

이들이 모두 같은 Authentication 객체를 받아들이나

이를 인증하는 방식은 다른 경우(ProvideManager이 다름)에 그렇다고 한다.  

 

이 내용을 실제 코드에 적용해보게 된다면 더 상세하게 정리해서 추가하겠다.

 

출처

https://docs.spring.io/spring-security/reference/servlet/authentication/architecture.html

 

Servlet Authentication Architecture :: Spring Security

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

docs.spring.io