코드스테이츠 백엔드 부트캠프 63일차 - Spring Security의 인증 컴포넌트

wish17·2023년 3월 17일
0
post-thumbnail

외부에서 가져온 모든 그림, 사진은 링크로 출처를 표기해 뒀습니다.
해당 그림, 사진을 누르며 출처로 이동합니다.

Spring Security의 인증 컴포넌트

Spring Security의 컴포넌트로 보는 인증(Authentication) 처리 흐름

  1. 클라이언트로부터의 인증 요청
  • 사용자가 로그인 폼을 통해 사용자 이름과 비밀번호를 입력하고 전송한다.
  1. AuthenticationFilter 처리
  • UsernamePasswordAuthenticationFilter클래스가 해당 요청을 전달 받는다.
  • UsernamePasswordAuthenticationFilter클래스는 사용자 이름과 비밀번호를 추출한다.
  • UsernamePasswordAuthenticationFilter클래스에 있는 attemptAuthentication() 메서드를 통해 인증 시도를 시작한다.
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    // 생략
}
  1. 인증 객체 생성
  • 필터는 사용자 이름과 비밀번호를 포함한 UsernamePasswordAuthenticationToken 객체를 생성한다.
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, password);
  1. AuthenticationManager 처리
  • AuthenticationManager 인터페이스를 통해 인증 처리를 진행한다.
  • authenticate() 메서드를 호출하여 인증 처리를 시작한다.
public interface AuthenticationManager {
    Authentication authenticate(Authentication authentication) throws AuthenticationException;
}
  1. AuthenticationProvider 처리
  • AuthenticationProvider에서 UserDetailsService를 사용하여 데이터베이스에서 사용자 정보를 조회한다.
    • UserDetails는 데이터베이스 등의 저장소에 저장된 사용자의 Username과 사용자의 자격을 증명해주는 크리덴셜(Credential)인 Password, 그리고 사용자의 권한 정보를 포함하고 있는 컴포넌트다.
    • 그리고 이 UserDetails 를 제공하는 컴포넌트가 바로 UserDetailsService다.
  • loadUserByUsername() 메서드를 호출하여 사용자 정보를 가져온다.
public interface UserDetailsService {
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}
  1. 인증 결과 생성
  • 인증이 성공하면, AuthenticationProvider는 인증된 사용자의 권한 정보를 포함한 새로운 UsernamePasswordAuthenticationToken 객체를 생성한다.
UsernamePasswordAuthenticationToken authenticatedToken = new UsernamePasswordAuthenticationToken(principal, credentials, authorities);
  1. SecurityContext에 인증 결과 저장
  • 생성된 인증 결과는 SecurityContextHolder에 저장된다.
SecurityContextHolder.getContext().setAuthentication(authenticatedToken);
  1. 필터 체인 계속 진행
  • 인증 처리가 끝나면, 필터 체인의 다음 필터들을 처리하며 요청이 처리된다.
  1. 인가 처리
  • 사용자의 요청이 처리되기 전에 인가 처리가 진행된다. 이 과정에서 사용자의 권한 정보와 요청된 리소스의 권한 정보를 비교하여 접근 허용 여부를 결정한다.
  1. 요청 처리
  • 인증과 인가가 모두 통과되면 요청이 서블릿이나 컨트롤러로 전달되어 처리된다.

핵심 정리

AuthenticationManager는 인증 처리를 총괄하는 매니저 역할을 하는 인터페이스이고, AuthenticationManager를 구현한 구현 클래스가 ProviderManager 이다.

  • UsernamePasswordAuthenticationFilter가 생성하는 Authentication은 인증을 위해 필요한 사용자의 로그인 정보를 가지고 있다.
  • AuthenticationProvider 가 생성한 Authentication은 인증에 성공한 사용자의 정보(Principal, Credential, GrantedAuthorities)를 가지고 있다.
  • 인증된 Authentication을 전달 받은 UsernamePasswordAuthenticationFilterSecurityContextHolder를 이용해 SecurityContext 에 인증된 Authentication을 저장한다.
  • SecurityContext는 이 후에 HttpSession 에 저장되어 사용자의 인증 상태를 유지한다.

Spring Security의 인증 컴포넌트

UsernamePasswordAuthenticationFilter

UsernamePasswordAuthenticationFilter 서블릿 필터(클래스)

  • 사용자 이름과 비밀번호를 기반으로 한 인증 처리를 수행하는 서블릿 필터다.
  • AbstractAuthenticationProcessingFilter를 상속받아 구현되어 있다.
  • 기본적으로 HTTP POST 요청에 대해서만 인증을 수행한다.
  • 사용자 이름과 비밀번호를 추출하여 UsernamePasswordAuthenticationToken 객체를 생성한다.
  • 추출한 사용자 이름과 비밀번호를 바탕으로 attemptAuthentication() 메서드를 호출한다.
  • 인증 결과로 받은 Authentication 객체를 성공적으로 인증된 사용자로 설정한다.
  • 인증이 성공적으로 이루어진 경우, successfulAuthentication() 메서드가 호출되어 인증 성공에 따른 후속 처리를 수행한다.
  • 인증 과정에서 실패할 경우, unsuccessfulAuthentication() 메서드를 호출하여 인증 실패에 따른 후속 처리를 수행한다.
  • 사용자 정의 인증 방식으로 확장할 수 있으며, 필요한 경우 해당 클래스를 상속받아 사용자 정의 필터를 구현할 수 있다.

AbstractAuthenticationProcessingFilter

AbstractAuthenticationProcessingFilter 서블릿 필터(클래스)

  • 이 클래스를 상속받아 인증 처리 과정을 구현할 수 있다. 예를 들어 UsernamePasswordAuthenticationFilter는 이 클래스를 상속받아 사용자 이름과 비밀번호를 기반으로 한 인증 처리를 구현한다.

  • HTTP 기반의 인증 요청을 처리하지만 실질적인 인증 시도는 하위 클래스에 맡기고, 인증에 성공하면 인증된 사용자의 정보를 SecurityContext에 저장하는 역할을 한다.

  • attemptAuthentication() 메서드는 인증을 시도하는 주요 메서드로, 구체적인 인증 로직은 이 메서드를 구현하여 작성한다.

  • 인증이 성공적으로 이루어진 경우, successfulAuthentication() 메서드가 호출되어 인증 성공에 따른 후속 처리를 수행한다.

  • 인증 과정에서 실패할 경우, unsuccessfulAuthentication() 메서드를 호출하여 인증 실패에 따른 후속 처리를 수행한다.

  • AbstractAuthenticationProcessingFilter는 서블릿 필터의 인터페이스를 구현하며, doFilter() 메서드를 오버라이딩하여 인증 처리를 수행하는 로직을 제공한다.

  • 사용자 정의 인증 처리를 구현하기 위해 이 클래스를 상속받아 필요한 메서드를 오버라이딩하고 확장할 수 있다.

UsernamePasswordAuthenticationToken

UsernamePasswordAuthenticationToken클래스

  • 사용자 이름과 비밀번호를 기반으로 한 인증 토큰 객체로 사용한다.

  • Authentication 인터페이스를 구현한다.

  • 주요 정보는 사용자 이름(principal), 비밀번호(credentials) 및 인증된 사용자의 권한(authorities)을 포함한다.

  • 인증 전과 인증 후의 토큰 상태를 나타낼 수 있다. 인증 전에는 사용자 이름과 비밀번호만 포함하며, 인증 후에는 권한 정보가 추가된다.

  • 인증 과정에서 UsernamePasswordAuthenticationToken 객체를 사용하여 사용자의 인증 정보를 전달하고, 인증 후에는 인증된 사용자의 권한 정보를 포함한 토큰 객체를 반환한다.

  • 인증 과정에서 사용되는 주요 인증 토큰으로, AuthenticationProviderUserDetails 객체와 함께 사용되어 인증 처리를 수행한다.

  • 인증 과정에서 발생하는 인증 요청 및 인증 결과의 표현을 담당하며, 인증 정보를 저장하고 전달하는 역할을 수행한다.

Authentication

Authentication 인터페이스

  • Spring Security에서 인증 정보를 표현하는 핵심 인터페이스이다.

  • 인증 정보는 주로 다음의 구성 요소를 포함한다.

    • Principal: 인증된 사용자의 주체를 나타내는 객체
    • Credentials: 인증에 사용된 자격증명 정보
    • Authorities: 인증된 사용자가 가지고 있는 권한 정보를 나타내는 객체
    • Details: 인증과 관련된 추가 정보를 포함하는 객체
  • 인증 과정 중에 Authentication 객체는 인증 요청의 표현 및 인증 결과의 저장을 담당한다.

  • 인증 처리기(AuthenticationManager)에서 인증을 진행한 후, 인증된 사용자의 정보와 권한을 포함한 Authentication 객체가 반환된다.

  • 인증된 사용자의 인증 정보는 SecurityContextHolder에 저장되어 애플리케이션 전반에 걸쳐 접근 가능하다.

  • 다양한 인증 방식에 따라 다양한 구현체를 가질 수 있다. 예를 들어, UsernamePasswordAuthenticationToken은 사용자 이름과 비밀번호 기반의 인증을 위한 구현체다.

AuthenticationManager

AuthenticationManager 인터페이스

  • 인증 처리를 총괄하는 매니저 역할을 하는 인터페이스

  • authenticate() 메서드 하나만 정의되어 있다.

    • authenticate() = 전달된 인증 정보를 기반으로 인증을 수행하고, 인증에 성공한 경우 인증된 사용자 정보와 권한을 포함한 Authentication 객체를 반환하는 메서드
  • 인증을 위한 Filter는 AuthenticationManager를 통해 느슨한 결합을 유지하고 있으며, 인증을 위한 실질적인 관리는 AuthenticationManager를 구현하는 구현 클래스를 통해 이루어진다.

    • 구현 클래스 ex = ProviderManager

ProviderManager

ProviderManager 클래스

  • AuthenticationManager 인터페이스를 구현하는 클래스다.

  • AuthenticationProvider를 관리하고, AuthenticationProvider에게 인증 처리를 위임하는 역할을 한다.

  • List<AuthenticationProvider> 객체를 DI 받는다.

  • authenticate() 메서드에서 DI 받은 List와 반복문을 이용해 적절한 AuthenticationProvider를 찾는다.

public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
  ...
  ...

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
  ...
  ...
  
	for (AuthenticationProvider provider : getProviders()) {
		if (!provider.supports(toTest)) {
			continue;
		}
		if (logger.isTraceEnabled()) {
				logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)",
				provider.getClass().getSimpleName(), ++currentPosition, size));
		}
		try { // 찾으면 해당 AuthenticationProvider에게 인증 처리를 위임
			result = provider.authenticate(authentication);
			if (result != null) {
				copyDetails(authentication, result);
				break;
			}
		}
		catch (AccountStatusException | InternalAuthenticationServiceException ex) {
			prepareException(ex, authentication);
			// SEC-546: Avoid polling additional providers if auth failure is due to
			// invalid account status
			throw ex;
		}
		catch (AuthenticationException ex) {
		lastException = ex;
		}	
	}
  ...
  ...    
}    
}
  • authenticate() 메서드에서 인증이 정상적으로 처리되면 인증에 사용된 Credentials를 제거한다.

AuthenticationProvider

AuthenticationProvider 인터페이스

  • 실질적인 인증 수행을 담당하는 컴포넌트다.

  • 주요 메서드로는 authenticate()supports()가 있다. authenticate() 메서드는 인증 요청을 처리하고, supports() 메서드는 해당 AuthenticationProvider가 특정 인증 유형을 지원하는지 확인한다.

  • 위에서 자세히 설명한 authenticate() 메서드는 전달된 인증 요청이 유효한지 검사하고, 인증된 사용자 정보와 권한을 포함한 Authentication 객체를 반환한다. 인증에 실패한 경우, AuthenticationException을 발생시킨다.

  • supports() 메서드는 인증 요청 객체의 유형이 해당 AuthenticationProvider에서 처리할 수 있는 유형인지 확인한다. 이를 통해 ProviderManager가 적절한 AuthenticationProvider를 선택하여 인증 처리를 진행한다.

  • 일반적으로 UserDetails를 로드(load)하는 UserDetailsService와 함께 사용된다. UserDetailsService는 사용자 정보를 로드하고, AuthenticationProvider는 로드된 사용자 정보와 클라이언트로부터 전달받은 인증 정보를 비교하여 인증을 수행한다.

  • Spring Security에서는 다양한 AuthenticationProvider 구현체를 제공한다. 예를 들어, DaoAuthenticationProvider는 사용자 이름과 비밀번호를 기반으로 하는 인증을 처리하며, OAuth2 인증 방식을 처리하는 OAuth2AuthenticationProvider도 있다.

UserDetails

UserDetails 인터페이스

  • 데이터베이스 등의 저장소에 저장된 사용자의 Username과 사용자의 자격을 증명해주는 크리덴셜(Credential)인 Password 그리고 사용자의 권한 정보를 포함하는 컴포넌트다.

  • 위에서 언급한 AuthenticationProviderUserDetails를 이용해 자격 증명을 수행한다.

  • 사용자 이름, 비밀번호, 권한, 계정 잠금 여부 등 인증에 필요한 사용자 정보를 포함한다.

  • 사용자 정보를 저장하고 관리하는 데 사용된다.

UserDetailsService

UserDetailsService 인터페이스

  • UserDetails 객체를 로드하는 인터페이스다.

  • loadUserByUsername(String username) 메서드를 구현해야 한다.

    • 이 메서드는 주어진 사용자 이름으로 UserDetails 객체를 로드한다.

SecurityContext와 SecurityContextHolder

SecurityContext인터페이스와 SecurityContextHolder클래스의 관계도는 아래그림과 같다.

Spring Security 입장에서는 SecurityContextHolder에 의해 SecurityContext에 값이 채워져 있다면 인증된 사용자로 간주한다.

  • SecurityContext는 인증된 사용자의 정보와 관련된 보안 정보를 저장하는 객체이다.

  • SecurityContextHolderSecurityContext 객체를 저장하고 관리하는 유틸리티 클래스이다.

  • SecurityContextHolder를 사용하면 현재 인증된 사용자의 정보를 가져오거나 수정할 수 있다.

  • SecurityContextHolder는 기본적으로 다음 세 가지 전략을 사용하여 SecurityContext를 관리한다.

    • MODE_THREADLOCAL (기본값): ThreadLocal에 SecurityContext를 저장하고 관리한다. 이 전략은 대부분의 일반적인 사용 사례에서 적합하다.
    • MODE_INHERITABLETHREADLOCAL: InheritableThreadLocal을 사용하여 SecurityContext를 저장하고 관리한다. 이 전략은 스레드간에 보안 컨텍스트를 상속받아야 하는 경우에 유용하다.
    • MODE_GLOBAL: 단일 SecurityContext 인스턴스를 사용하여 모든 스레드에서 동일한 보안 컨텍스트를 공유한다. 이 전략은 특수한 경우에만 사용되어야 한다.
  • Spring Security에서 인증이 완료되면, 인증된 사용자의 정보를 담고 있는 Authentication 객체를 SecurityContext에 저장한다. 이후에는 SecurityContextHoldergetContext() 메서드를 통해 현재 인증된 사용자의 정보를 가져올 수 있다.

  • SecurityContextHolder.clearContext() 메서드를 사용하여 현재 스레드의 SecurityContext를 초기화할 수 있다. 이 메서드는 보통 로그아웃 처리 시에 사용된다.

컴포넌트 핵심 포인트

  • UsernamePasswordAuthenticationFilter 는 클라이언트로부터 전달받은 Username과 Password를 Spring Security가 인증 프로세스에서 이용할 수 있도록 UsernamePasswordAuthenticationToken 을 생성한다.

  • AbstractAuthenticationProcessingFilter는 HTTP 기반의 인증 요청을 처리하지만 실질적인 인증 시도는 하위 클래스에 맡기고, 인증에 성공하면 인증된 사용자의 정보를 SecurityContext에 저장하는 역할을 한다.

  • Authentication은 Spring Security에서의 인증 자체를 표현하는 인터페이스이다.

  • AuthenticationManager는 이름 그대로 인증 처리를 총괄하는 매니저 역할을 하는 인터페이스이며, 인증을 위한 실질적인 관리는 AuthenticationManager를 구현하는 구현 클래스(ProviderManager)를 통해 이루어진다.

  • ProviderManagerAuthenticationProvider를 관리하고, AuthenticationProvider에게 인증 처리를 위임하는 역할을 한다.

  • AuthenticationProviderAuthenticationManager로부터 인증 처리를 위임받아 실질적인 인증 수행을 담당하는 컴포넌트이다.

  • UserDetails 는 데이터베이스 등의 저장소에 저장된 사용자의 Username과 사용자의 자격을 증명해주는 크리덴셜(Credential)인 Password 그리고 사용자의 권한 정보를 포함하는 컴포넌트이며, AuthenticationProviderUserDetails를 이용해 자격 증명을 수행한다.

  • UserDetailsServiceUserDetails를 로드(load)하는 핵심 인터페이스이다.

  • SecurityContext는 인증된 Authentication 객체를 저장하는 컴포넌트이고, SecurityContextHolderSecurityContext를 관리하는 역할을 담당한다.


TMI

필터(Filter)

DelegatingFilterProxy

DelegatingFilterProxy는 Spring Security의 필터 체인을 다른 서블릿 컨테이너에 적용하기 위해 사용된다. 이 필터는 web.xml 파일에서 설정된 필터를 찾아 그 필터를 실행하는 역할을 한다. 이를 통해 Spring Security의 필터 체인을 다른 서블릿 컨테이너에서도 적용할 수 있다. 또한, DelegatingFilterProxy는 서블릿 컨테이너에서 필터 체인을 직접 관리하기 때문에 필터의 라이프사이클 관리를 Spring에 위임할 수 있다.

0개의 댓글