본문 바로가기
Spring/Spring Security

[Spring Security] 전반적 Flow

by seoyamin 2023. 1. 21.

1. Spring Security란 ?

Spring Security는 Spring의 하위 프레임워크로,
인증(Authentication)권한(Authorization) 처리를 지원한다. 

" 기능에 대한 접근을 제어한다 "

해당 과정에서 발생하는 보안적 이슈 문제 또한 지원한다.

 

1-1. Spring Security와 Filter

Spring Security는 Spring MVC life cycle의 Filter단에서 이루어진다.

Spring MVC Request Life Cycle

 

이때, Http Request는 실제로 여러개의 Filter를 거치게 되며, 이러한 필터 여러개가 마치 체인처럼 엮여있다고 해서 Filter Chain이라고 부른다. 

 

1-2. Authentication vs. Authorization

Authentication (인증) Authorization (인가)
접근 주체가 누구인지를 확인하는 과정  인증을 마친 사용자가 자원을 요청할 때,
이를 이용할 자격이 있는 지 확인하는 과정
Authentication is how we verify the identity of who is trying to access a particular resource.
Authorization is determining who is allowed to access a particular resource.
로그인  자원 = api 등

- principal : 접근 주체

- credential : 비밀번호

 

1-3. Spring Security 시작하기

# Gradle

// build.gralde

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-security'
}

 


 

2. Spring Security Flow

Spring Security Authentication Architecture

 

①  접근 주체가 Http Request를 통해 로그인 정보를 보내며 인증 요청을 한다.

 

②  AuthenticationFilter①의 요청을 인터셉트한 후, 로그인 정보에 대한 유효성 검사를 한다. 

     이후 HttpServletRequest에서 꺼내온 정보를 UserPasswordAuthenticationToken으로 만들고,

     인증 담당 AuthenticationManager (구현체 : ProviderManager)에게 전달한다. 

 

③  AuthenticationManager의 구현체 ProviderManager

     AuthenticationFilter에게서 UserPasswordAuthenticationToken을 전달받음

 

④  실제로 인증을 진행할 AuthenticationProvider에게 UserPasswordAuthenticationToken을 전달한다.

 

⑤  UserDetailsService가 직접 Database에서 사용자의 인증 관련 정보를 (아이디, pw, 권한 등)

      UserDetails라는 객체 형태로 가져온다.

 

⑥  AuthenticationProvider①에서 입력된 정보와 ⑤에서 가져온 정보를 비교해서 인증을 처리한다.

 

⑦ ~ ⑩  인증이 완료되면  AuthenticationProvider가 Authentication 객체를 반환하고,

                이를 받아서 SecurityContextHolder에 담은 후 AuthenticationSuccessHandle을 실행

               (인증 실패 시 AuthenticationFailureHandle 실행)

 

 


 

3. Spring Security 구성 모듈

  • principal의 관리자 = Authentication
  • Authentication의 관리자 = SecurityContext
  • SecurityContext의 관리자 = SecurityContextHolder

 

3-1. Autentication

현재 접근 추체의 정보와 권한을 담은 인터페이스를 Authentication이라고 한다.

모든 접근 주체는 Authentication을 생성하며, 생성된 Authentication은 SecurityContext에 저장된다.

Spring Security에서는 SecurityContextHolder를 통해서 SecurityContext에 접근 후, Authentication에 접근할 수 있다.

public interface Authentication extends Principal, Serializable {
    Collection<? extends GrantedAuthority> getAuthorities(); // 인증된 현재 사용자의 권한 목록
    Object getCredentials();   // credential (대부분 pw) GET
    Object getDetails();       // 상세정보 GET
    Object getPrincipal();     // principal 객체 GET
    boolean isAuthenticated(); // 인증 여부 GET
    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException; // 인증 여부 설정
}

 

3-2. SecurityContext

Authentication 객체를 저장하는 역할을 하는 클래스이다.

ThreadLocal에 저장되므로, 아무데서나 SecurityContext를 참조할 수 있게 한다.

인증 완료 시 HttpSession에 저장되므로 어플 어디서든 전역적으로 참조할 수 있다. 

 

3-3. SecurityContextHolder

current execution thread와 SecurityContext를 연결한다.

이때, 실행중인 스레드가 달라지면 제대로 된 인증 정보를 가져올 수 없다. 따라서 이를 방지하기 위해 런타임시에 스레드와 SecurityContext를 어떻게 연결할 지 2가지 전략 중 택 1을 활용한다.

 

  • SecurityContextHolder.MODE_INHERITABLETHREADLOCAL메인 스레드와 자식 스레드가 동일한 SecurityContext를 공유
  • SecurityContextHolder.MODE_THREADLOCAL각 스레드마다 SecurityContext를 할당한다. 따라서 자식 스레드와 SecurityContext를 공유 X

 

3-4. UserPasswordAuthenticationToken

Authentication을 상속 받은 AuthenticationToken를 상속 받은 하위 클래스이다.

즉, 인증이 완료되면 SecurityContext에 등록될 Authentication 객체로 이해할 수 있다. 

 

UserPasswordAuthenticationToken은 2개의 기본 생성자를 가지고 있다. 첫번째 생성자는 아직 인증되지 않은 Authentication 객체를, 두번째 생성자는 인증이 완료된 Authentication 객체를 생성한다.

 

public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {

    private final Object principal;
    private Object credentials;
    
    // 인증 완료 전의 Authentication 객체 생성
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
		super(null);
		this.principal = principal;
		this.credentials = credentials;
		setAuthenticated(false);
	}
    
    // 인증 완료 후의 Authentication 객체 생성
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
			Collection<? extends GrantedAuthority> authorities) {
		super(authorities);
		this.principal = principal;
		this.credentials = credentials;
		super.setAuthenticated(true); // must use super, as we override
	}
}