본문 바로가기
web/spring

Spring Security(2) - 주요 모듈

by su0a 2024. 2. 3.

Spring Security 주요 모듈

 

1) SecurityContextHolder

  • 보안 주체의 세부정보를 포함하여 응용 프로그램의 현재 보안 컨텍스트에 대한 세부정보가 저장된다.

2) SecurityContext

  • Authentication을 보관하는 역할을 하며 SecurityContext를 통해 Authentication객체를 꺼낼 수 있다.

3) Authentication

  • 현재 접근하는 주체의 정보와 권한을 담는 인터페이스이다.
public interface Authentication extends Principal, Serializable {
    // 현재 사용자의 권한 목록을 가져옴
    Collection<? extends GrantedAuthority> getAuthorities();
    
    // credentials(주로 비밀번호)을 가져옴
    Object getCredentials();
    
    Object getDetails();
    
    // Principal 객체를 가져옴.
    Object getPrincipal();
    
    // 인증 여부를 가져옴
    boolean isAuthenticated();
    
    // 인증 여부를 설정함
    void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

 

4) UsernamePasswordAuthenticationToken

  • Authentication을 implements한 AbstractAuthenticationToken의 하위 클래스로, User의 username이 Principal역할을 하고, password가 credential 역할을 한다. 
  • UsernamePasswordAuthenticationToken의 첫번째 생성자는 인증전의 객체를 생성하고, 두번째 생성자는 인증이 완료된 객체를 생성한다.
public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
    // 주로 사용자의 ID에 해당함
    private final Object principal;
    // 주로 사용자의 PW에 해당함
    private Object credentials;
    
    // 인증 완료 전의 객체 생성
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
		super(null);
		this.principal = principal;
		this.credentials = credentials;
		setAuthenticated(false);
	}
    
    // 인증 완료 후의 객체 생성
    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
	}
}


public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
}

 

5) AuthenticatioinProvider

  • 인증전의 Authentication객체를 받아서 인증이 완료된 객체를 반환하는 역할을 한다.
public interface AuthenticationProvider {

	// 인증 전의 Authenticaion 객체를 받아서 인증된 Authentication 객체를 반환
    Authentication authenticate(Authentication var1) throws AuthenticationException;

    boolean supports(Class<?> var1);
    
}
  • AuthenticationProvider 커스텀 하는법 : 아래와 같은 AuthenticationProvider 인터페이스를 커스텀한 후 AuthenticationManager에 등록
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
    @Bean
    public AuthenticationManager getAuthenticationManager() throws Exception {
        return super.authenticationManagerBean();
    }
      
    @Bean
    public CustomAuthenticationProvider customAuthenticationProvider() throws Exception {
        return new CustomAuthenticationProvider();
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(customAuthenticationProvider());
    }
}

 

6) AuthenticationManager

  • AthenticationManager에 등록된 AuthenticationProvider에 의해 인증이 처리된다. 
  • 인증에 성공하면 Athentication 2번째 생성자를 이용해 인증에 성공한 객체를 생성한다.
public interface AuthenticationManager {
	Authentication authenticate(Authentication authentication) 
		throws AuthenticationException;
}

 

7) ProviderManager

  • AuthenticationManager을 implements한 클래스이다.
  • 실제 인증과정에 대한 로직을 가지고 있는 AuthenticationProvider을 List로 가지고 있으며, for문을 통해 모든 AuthenticationProvider을 조회하면서 인증을 처리한다.
public class ProviderManager implements AuthenticationManager, MessageSourceAware,
InitializingBean {
    public List<AuthenticationProvider> getProviders() {
		return providers;
	}
    public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		Class<? extends Authentication> toTest = authentication.getClass();
		AuthenticationException lastException = null;
		Authentication result = null;
		boolean debug = logger.isDebugEnabled();
        //for문으로 모든 provider를 순회하여 처리하고 result가 나올 때까지 반복한다.
		for (AuthenticationProvider provider : getProviders()) {
            ....
			try {
				result = provider.authenticate(authentication);

				if (result != null) {
					copyDetails(authentication, result);
					break;
				}
			}
			catch (AccountStatusException e) {
				prepareException(e, authentication);
				// SEC-546: Avoid polling additional providers if auth failure is due to
				// invalid account status
				throw e;
			}
            ....
		}
		throw lastException;
	}
}

 

8) UserDetails

  • 인증에 성공하여 생성된 UserDetails객체는 Authentication객체를 구현한 usernamePasswordAuthenticationToken을 생성하기 위해 사용된다.
public interface UserDetails extends Serializable {

    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword();

    String getUsername();

    boolean isAccountNonExpired();

    boolean isAccountNonLocked();

    boolean isCredentialsNonExpired();

    boolean isEnabled();
    
}

 

9) UserDetailsService

  • UserDetails객체를 반환하는 메소드를 가지고 있다.
public interface UserDetailsService {

    UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;

}
  • 보통 이를 구현한 클래스 내부에 UserRepository를 주입받아 DB와 연결하여 처리한다.
public class UserDetailService implements UserDetailsService {

    private final UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByLoginId(username)
                .orElseThrow(() -> {
                    return new UsernameNotFoundException("해당 유저를 찾을 수 없습니다.");
                });

        // Security의 세션에 유저 정보가 저장됨
        return new UserDetail(user);
    }
}