[Spring] 스프링 시큐리티 인증, 인가 인터페이스 살펴보기

maxxyoung·2024년 3월 16일
0

너어어어어는 인증도 네가하고 인가도 너가 다 만드니? 스프링은 모든게 준비되어 있단다.

스프링 시큐리티 아키텍처

스프링 시큐리티에서 중요하게 생각해야할 것은 인증(너 누구니?), 인가(뭘 허용하고 싶니?)다. 그리고 스프링 시큐리티는 이 둘의 확장 전략을 모두 갖춰놨다.

스프링부트 3.X, 스프링시큐리트 6.X 부터 설정이 변경되었다. 하지만 안의 동작이 달라진건 아니므로 이해하는데 있어 꼭 필요한 부분만 정리하겠다.

인증(Authentication)

간단하게 보면 AuthenticationManagerProvider를 호출하여 인증을 위임한다라고 할 수 있겠다.

가장 중요한 인터페이스는 AuthenticationManager이고 다음과 같이 하나의 메소드를 가지고 있다.

public interface AuthenticationManager {

  Authentication authenticate(Authentication authentication)
    throws AuthenticationException;
}

구현체로 ProviderManager가 있고 AuthenticationProvider(인터페이스)로 인증을 위임하는 형태로 동작한다.

public interface AuthenticationProvider {

	Authentication authenticate(Authentication authentication)
			throws AuthenticationException;

	boolean supports(Class<?> authentication);
}

인증 방법이 여러 개가 있을 것이고 각각의 Provider 구현체가 있다.

  • 기본 인증 (Basic Authentication):
    사용자 이름과 비밀번호를 요청 헤더에 Base64로 인코딩하여 전송하는 방식입니다.
    Spring Security에서는 BasicAuthenticationProvider가 이를 처리합니다.
  • 폼 기반 인증 (Form-based Authentication):
    웹 애플리케이션에서 사용자가 제공한 사용자 이름과 비밀번호를 사용하여 인증합니다.
    Spring Security에서는 DaoAuthenticationProvider가 이를 처리합니다.
  • 토큰 기반 인증 (Token-based Authentication):
    사용자가 토큰을 발급받아 이를 사용하여 인증합니다.
    주로 JWT(Json Web Token)이 사용되며, Spring Security에서는 JwtAuthenticationProvider가 이를 처리합니다.
  • OAuth 인증 (OAuth Authentication):
    OAuth 프로토콜을 사용하여 인증을 수행합니다.
    Spring Security에서는 OAuth2AuthenticationProvider가 이를 처리합니다.

다음은 Manager 구현체의 소스이다. support메소드의 경우 Provider 클래스를 체크하여 지원하지 않는 Provider라면 건너띄게 된다.
다음의 소스는 ProviderManager의 일부이다.

@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		Class<? extends Authentication> toTest = authentication.getClass();
		AuthenticationException lastException = null;
		AuthenticationException parentException = null;
		Authentication result = null;
		Authentication parentResult = null;
		int currentPosition = 0;
		int size = this.providers.size();
		for (AuthenticationProvider provider : getProviders()) {
			if (!provider.supports(toTest)) {
				continue;
			}
...

url과 같이 리소스를 그룹지었을 때, url의 각 그룹들은 각각의 AuthenticationManager를 가질 수 있다. 주로 ProviderManager 구현체를 부모로 갖는다.
(예를 들면 유저용, 어드민용 리소스가 그렇다. /user/**, /admin/**용으로 매니저를 각각 만들어 각 리소스에 맞게 인증을 진행할 수 있다.)

인가(Authrization)

시큐리티 6버전에서 AccessDecisionManager와 Voter(AuthenticationManager와 Provider 관계)가 AuthorizationManager로 통합되었다. 시큐리티 6버전에서 SecurityFilterChain bean을 만들 때 같이 설정할 수 있다. 다음은 AuthorizationManager 클래스이다.

@FunctionalInterface
public interface AuthorizationManager<T> {

	default void verify(Supplier<Authentication> authentication, T object) {
		AuthorizationDecision decision = check(authentication, object);
		if (decision != null && !decision.isGranted()) {
			throw new AccessDeniedException("Access Denied");
		}
	}

	@Nullable
	AuthorizationDecision check(Supplier<Authentication> authentication, T object);

}

스프링시프링 시큐리티는 인증을 마치고 나면 인증 정보를 담는 SecurityContext를 반환하여 스레드로컬에 저장한다. 그리고 다음에 오는 요청을 스프링시큐리티가 판단하여 이 요청과 스레드 로컬에 저장된 인증 정보를 연결하여 스프링 내에서 유저정보를 쉽게 꺼내볼 수 있게 해준다.

스프링 시큐리티 아키텍처 공식문서

profile
오직 나만을 위한 글. 틀린 부분 말씀해 주시면 감사드립니다.

0개의 댓글