본문 바로가기

TIL

[Spring Security][인증 컴포넌트]

728x90

Spring Security의 커포넌트로 보는 인증 처리 흐름

UsernamePasswordAuthenticationFilter

사용자의 로그인 request를 제일 먼저 만나는 컴포넌트는 Spring Security Filter Chain의 UsernamePasswordAuthenticatonFilter이다.

UsernamePasswordAuthenticationFilter는 일반적으로 로그인 폼에서 제출되는 Username과 Password를 통한 인증을 처리하는 Filter이다

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

public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { // (1)

	public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username"; // (2)

	public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password"; // (3)

	private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login","POST"); // (4)

  ...
  ...

	public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) {
		super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager); // (5)
	}

  // (6)
	@Override
	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException {
    // (6-1)
		if (this.postOnly && !request.getMethod().equals("POST")) {
			throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
		}

		String username = obtainUsername(request);
    ...

		String password = obtainPassword(request);
    ...
		
    // (6-2)
    UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
		...

		return this.getAuthenticationManager().authenticate(authRequest); // (6-3)
	}

	...
  ...
}

UsernamePasswordAuthenticationFilter의 코드 일부이다

  • UsernamePasswordAuthenticationFilter는 1)과 같이 AbstractAuthenticationProcessingFilter를 상속한다
    UsernamePasswordAuthenticationFilter 클래스의 이름이 Filter로 끝나지만 UsernamePasswordAuthenticationFilter클래스에는 doFilter() 메서드가 존재하지 않는다
    Filter가 Filter의 역할을 하기위해서는 doFilter()메서드가 있어야 하기때문에 다른곳에있는데 상위클래스인 AbstractAuthenticationProcessingFilter 클래스가 doFilter()메서드를 포함하고있다
    결과적으로 사용자의 로그인 request를 제일 먼저 전달 받는 클래스는 UsernamePasswordAuthenticationFilter의 상위 클래스인 AbstractAuthenticationProcessingFilter 클래스인것이다.
  • 2)와 3)을 통해 클라이언트의 로그인 폼을 통해 전송되는 request parameter의 디폴트 name은 username과 password라는 것을 알 수 있다.
  • 4)의 AntPathRequestMatcher는 클라이언트의 URL에 매치되는 매처이다
    4)를 통해 클라이언트의 URL이 /login이고 HTTP Method가 POST일 경우 매치 될거라는 사실을 예측할 수 있다.
    4)에서 생성되는 AntPathRequestMatcher의 객체(DEFAULT_ANT_PATH_REQUEST_MATCHER)는 5)에서 상위 클래스인 AbstractAuthenticationProcessingFilter 클래스에 전달되어 Filter가 구체적인 작업을 수행할지 특별한 작업 없이 다른 Filter를 호출할지 결정하는데 사용된다.
  • 5)에서 AntPathRequestMatcher의 객체(DEFAULT_ANT_PATH_REQUEST_MATCHER)와 AuthenticationManager를 상위 클래스인 AbstractAuthenticationProcessingFilter에 전달한다
  • 6)의 attemptAuthentication() 메서드는 메서드 이름에서도 알수 있듯이 클라이언트에서 전달한 username과 password 정보를 이용해 인증을 시도하는 메서드이다.
    attemptAuthentication() 메서드는 상위 클래스인 AbstractAuthenticationProcessingFilter의 doFilter() 메서드에서 호출되는데 Filter에서 어떤 처리를 하는 시작점은 doFilter() 이다
    6-1)에서 HTTP Method가 POST가 아니면 Exception을 throw한다는 사실을 알 수 있다
    6-2)에서 클라이언트에서 전달한 username과 password정보를 이용해 UsernamePasswordAuthenticationToken을 생성한다
    여기서 UsernamePasswordAuthenticationToken은 인증을 하기 위해 필요한 인증 토큰이지 인증에 성공한 인증토큰과 상관이없다.
    6-3)에서 AuthenticationManager의 authenticate()메서드를 호출해 인증 처리르 위임하는 것을 볼 수 있다.

AbstractAuthenticationProcessingFilter

AbstractAuthenticationProcessingFilter클래스는 UsernamePasswordAuthenticationFilter가 상속하는 상위 클래스로써 Spring Security에서 제공하는 Filter중 하나이다

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

public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
		implements ApplicationEventPublisherAware, MessageSourceAware {

	...
  ...

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
	}

  // (1)
	private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws IOException, ServletException {
    // (1-1)
		if (!requiresAuthentication(request, response)) {
			chain.doFilter(request, response);
			return;
		}
		try {
			Authentication authenticationResult = attemptAuthentication(request, response); // (1-2)
			if (authenticationResult == null) {
				// return immediately as subclass has indicated that it hasn't completed
				return;
			}
			this.sessionStrategy.onAuthentication(authenticationResult, request, response);
			// Authentication success
			if (this.continueChainBeforeSuccessfulAuthentication) {
				chain.doFilter(request, response);
			}
			successfulAuthentication(request, response, chain, authenticationResult); // (1-3)
		}
		catch (InternalAuthenticationServiceException failed) {
			this.logger.error("An internal error occurred while trying to authenticate the user.", failed);
			unsuccessfulAuthentication(request, response, failed);  // (1-4)
		}
		catch (AuthenticationException ex) {
			// Authentication failed
			unsuccessfulAuthentication(request, response, ex);
		}
	}

	
  // (2)
	protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
		if (this.requiresAuthenticationRequestMatcher.matches(request)) {
			return true;
		}
		if (this.logger.isTraceEnabled()) {
			this.logger
					.trace(LogMessage.format("Did not match request to %s", this.requiresAuthenticationRequestMatcher));
		}
		return false;
	}

  ...
  ...

  // (3)
	protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
			Authentication authResult) throws IOException, ServletException {
		SecurityContext context = SecurityContextHolder.createEmptyContext();
		context.setAuthentication(authResult);
		SecurityContextHolder.setContext(context);
		this.securityContextRepository.saveContext(context, request, response);
		if (this.logger.isDebugEnabled()) {
			this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
		}
		this.rememberMeServices.loginSuccess(request, response, authResult);
		if (this.eventPublisher != null) {
			this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
		}
		this.successHandler.onAuthenticationSuccess(request, response, authResult);
	}

	
  // (4)
	protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException failed) throws IOException, ServletException {
		SecurityContextHolder.clearContext();
		this.logger.trace("Failed to process authentication request", failed);
		this.logger.trace("Cleared SecurityContextHolder");
		this.logger.trace("Handling authentication failure");
		this.rememberMeServices.loginFail(request, response);
		this.failureHandler.onAuthenticationFailure(request, response, failed);
	}

  ...
  ...
}

AbstractAuthenticationProcessingFilter클래스코드

  • 1)을 통해 AbstractAuthenticationProcessingFilter 클래스가 Spring Security의 Filter임을 알 수 있다.
    1-1)에서는 AbstractAuthenticationProcessingFilter 클래스가 인증 처리를 해야되는지 아니면 다음 Filter를 호출할지 여부를 결정하고 있다
    1-1)에서 호출하는 requiresAuthentication()메서드는 2)에서 확인할 수 있듯이 하위 클래스에서 전달받은 requiresAuthenticationRequestMatcher 객체를 통해 들어오는 요청이 인증 처리를 해야되는지 여부를 결정하고 있다
    UsernamePasswordAuthenticationFilter의 코드의 AntPathRequestMatcher("/login",POST")의 파라미터인 URL과 HTTP Method가 매칭 조건이 된다는것을 참고해야한다
    1-2)에서 하위 클래스에 인증을 시도해 줄것을 요청하고 있다 여기서의 하위 클래스는 UsernamePasswordAuthenticationFilter가 된다
  • 1-3)에서 인증에 성공하면 처리할 동작을 수행하기 위해 successfulAuthentication()메서드를 호출한다
    successfulAuthentication()메서드는 3)에서 확인할 수 있다시피 인증에 성공한 이후 SecurityContextHolder를 통해 사용자의 인증 정보를 SecurityContext에 저장한 뒤, SecurityContext를 HttpSession에 저장한다
  • 만약 인증에 실패하면 1-4)와 같이 unsuccessfulAuthentication()메서드를 호출해 인증 실패 시 처리할 동작을 수행한다
    unsuccessfulAuthentication()메서드는 4)에서 확인할 수 있다시피 SecurityContext를 초기화하고, AuthenticationFailureHandler를 호출한다

UsernamePasswordAuthenticationToken

UsernamePasswordAuthenticationToken은 Spring Security에서 Username/Password로 인증을 수행하기 위해 필요한 토큰이며, 또한 인증 성공 후 인증에 성공한 사용자의 인증정보가 UsernamePasswordAuthenticationToken에 포함되어 Authentication 객체 형태로 SecurityContext에 저장된다.

public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {

	...

	private final Object principal;

	private Object credentials;

  ...
  ...

	
  // (1)
	public static UsernamePasswordAuthenticationToken unauthenticated(Object principal, Object credentials) {
		return new UsernamePasswordAuthenticationToken(principal, credentials);
	}

	
  // (2)
	public static UsernamePasswordAuthenticationToken authenticated(Object principal, Object credentials,
			Collection<? extends GrantedAuthority> authorities) {
		return new UsernamePasswordAuthenticationToken(principal, credentials, authorities);
	}

  ...
  ...

}

UsernamePasswordAuthenticationToken 클래스의 코드 일부

UsernamePasswordAuthenticationToken은 두개의 필드를 가지고있다 principal은 Username등의 신원을 의미하고, credentials는 Password를 의미한다

1)의 unauthenticated()메서드는 인증에 필요한 용도의 UsernamePasswordAuthenticationToken 객체를 생성하고,
2)의 authenticated()메서드는 인증에 성공한 이후 SecurityContext에 저장될 UsernamePasswordAuthenticationToken 객체를 생성한다.

 

Authentication

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

앞서 UsernamePasswordAuthenticationToken은 AbstractAuthenticationToken 추상 클래스를 상속하는 확장 클래스이자 Authentication 인터페이스의 메서드 일부를 구현하는 구현 클래스이기도 하다

 

애플리케이션의 코드 상에서 인증을 위해 생성되는 인증 토큰 또는 인증 성공 후 생성되는 토큰은 UsernamePasswordAuthenticationToken과 같은 하위 클래스의 형태로 생성되지만 생성된 토큰을 리턴 받거나 SecurityContext에 저장될 경우에 Authentication 형태로 리턴 받거나 저장된다.

public interface Authentication extends Principal, Serializable {
	Collection<? extends GrantedAuthority> getAuthorities();
	Object getCredentials();
	Object getDetails();
	Object getPrincipal();
	boolean isAuthenticated();
	void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

Authentication 인터페이스 코드

  • Principal
    사용자를 식별하는 고유정보이다.
    일반적으로 Username/Password 기반 인증에서 Username이 Principal이 되며, 다른 인증방식에서는 UserDetails가 Principal이 된다
  • Credentials
    사용자 인증에 필요한 Password를 의미하며 인증이 이루어지고 난 직후, ProviderMananger가 해당 Credentials를 삭제한다
  • Authorities
    AuthenticationProvider에 의해 부여된 사용자의 접근 권한 목록이며 일반적으로 GrantedAuthority 인터페이스의 구현 클래스는 SimpleGrantedAuthority 이다.

AuthenticationManager

AuthenticationManager는 이름 그대로 인증 처리를 총괄하는 매니저 역할을 하는 인터페이스이다.

public interface AuthenticationManager {

	Authentication authenticate(Authentication authentication) throws AuthenticationException;

}

AuthenticationManager 인터페이스

AuthenticationManager에는 authenticate()메서드 하나만 정의되어 있다 

인증을 위한 Filter는 AuthenticationManager를 통해 느슨한 결합을 유지하고 있으며, 인증을 위한 실질적인 관리는 AuthenticationManger를 구현하는 구현 클래스를 통해 이루어진다.

 

ProviderManager

AuthenticationManager를 구현하는 것은 어떤 클래스든 가능하지만 Spring Security에서 AuthenticationManager 인터페이스의 구현 클래스라고 하면 일반적으로 ProviderManager를 가리킨다.

ProviderManger는 이름에서 유추할 수 있듯이 AuthenticationProvider를 관리하고 AuthenticationProvider에게 인증 처리를 위임하는 역할을 한다

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

  // (1)
	public ProviderManager(List<AuthenticationProvider> providers, AuthenticationManager parent) {
		Assert.notNull(providers, "providers list cannot be null");
		this.providers = providers;
		this.parent = parent;
		checkState();
	}

  ...
  ...

	@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();

    // (2)
		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 {
				result = provider.authenticate(authentication);  // (3)
				if (result != null) {
					copyDetails(authentication, result);
					break;
				}
			}
			catch (AccountStatusException | InternalAuthenticationServiceException ex) {
				prepareException(ex, authentication);
				throw ex;
			}
			catch (AuthenticationException ex) {
				lastException = ex;
			}
		}

		...
    ...

		if (result != null) {
			if (this.eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) {
				((CredentialsContainer) result).eraseCredentials(); // (4)
			}

			if (parentResult == null) {
				this.eventPublisher.publishAuthenticationSuccess(result);
			}

			return result;
		}
    
    ...
    ...
	}

  ...
  ...
}

ProviderManager 클래스 코드

  • 1)에서 ProviderManager 클래스가 Bean으로 등록시, List<AuthenticationProvider>객체를 DI 받는다
  • DI 받은 List를 이용해 2)와 같이 for문으로 적절한 AuthenticationProvider를 찾는다
  • 적절한 AuthenticationProvider를 찾았다면 3)과 같이 해당 AuthenticationProvider에게 인증 처리를 위임한다
  • 인증이 정상적으로 처리되었다면 4)와 같이 인증에 사용된 Credentials를 제거한다

AuthenticationProvider

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

Username/Password기반의 인증 처리는 DaoAuthenticationProvider가 담당하고 있으며, DaoAuthenticationProvider는 UserDetailsService로부터 전달 받은 UserDetails를 이용해 인증을 처리한다.

public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { // (1)
  ...
  ...

	private PasswordEncoder passwordEncoder;

	...
  ...

  // (2)
	@Override
	protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		prepareTimingAttackProtection();
		try {
			UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username); // (2-1)
			if (loadedUser == null) {
				throw new InternalAuthenticationServiceException(
						"UserDetailsService returned null, which is an interface contract violation");
			}
			return loadedUser;
		}
		catch (UsernameNotFoundException ex) {
			mitigateAgainstTimingAttack(authentication);
			throw ex;
		}
		catch (InternalAuthenticationServiceException ex) {
			throw ex;
		}
		catch (Exception ex) {
			throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
		}
	}

  // (3)
	@Override
	protected void additionalAuthenticationChecks(UserDetails userDetails,
			UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
		if (authentication.getCredentials() == null) {
			this.logger.debug("Failed to authenticate since no credentials provided");
			throw new BadCredentialsException(this.messages
					.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
		}
		String presentedPassword = authentication.getCredentials().toString();
		if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { // (3-1)
			this.logger.debug("Failed to authenticate since password does not match stored value");
			throw new BadCredentialsException(this.messages
					.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
		}
	}

  ...
  ...
}

DaoAuthenticationProvider클래스 코드 일부

AuthenticationProvider 인터페이스의 구현 클래스를 확장하는 DaoAuthenticationProvider 클래스 코드의 일부이다.

  • 1)을보면 DaoAuthenticationProvider는 AbstractUserDetailsAuthenticationProvider를 상속하고있다
    AuthenticationProvider 인터페이스의 구현 클래스는 AbstractUserDetailsAuthenticationProvider이고,DaoAuthenticationProvider는 AbstractUserDetailsAuthenticationProvider를 상속한 확장 클래스이다
    따라서, AbstractUserDetailsAuthenticationProvider 추상클래스의 authenticate() 메서드에서부터 실질적인 인증 처리가 시작된다.
  • 2)의 retrieveUser()메서드는 UserDetailsService로부터 UserDetails를 조회하는 역할을 한다 조회된 UserDetails는 사용자를 인증하는데 사용될 뿐만아니라 인증에 성공할 경우, 인증된 Authentication 객체를 생성하는데 사용된다
    2-1)의this.getUserDetailsService().loadUserByUsername(username); 에서 UserDetails를 조회하고 있다
  • 3)additionalAuthenticationChecks() 메서드에서 PasswordEncoder를 이용해 사용자의 패스워드를 검증하고있다
    3-1)에서 클라이언트로부터 전달받은 패스워드와 데이터베이스에서 조회한 패스워드가 일치하는지 검증하고 있다

DaoAuthenticationProvider와 AbstractUserDetailsAuthenticationProvider의 코드를 이해하기 위해서는 메서드가 호출되는 순서가 중요하다 두 클래스가 번갈아가면서 호출되기 때문에 로직을 이해하기 쉽지않으므로 메서드가 호출되는 순서를 정리했다.

  1. AbstractUserDetailsAuthenticationProvider의 authenticated()메서드 호출
  2. DaoAuthenticationProvider의 retrieveUser()메서드 호출
  3. DaoAuthenticationProvider의 additionalAuthenticationCheck()메서드 호출
  4. DaoAuthenticationProvider의 createSuccessAuthentication() 메서드호출
  5. AbstractUserDetailsAuthenticationProvider의 createSuccessAuthentication()메서드 호출
  6. 인증된 Authentication을 ProviderManager에게 리턴

UserDetails

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

public interface UserDetails extends Serializable {

	Collection<? extends GrantedAuthority> getAuthorities(); // (1) 권한 정보
	String getPassword(); // (2) 패스워드
	String getUsername(); // (3) Username

	boolean isAccountNonExpired();  // (4)
	boolean isAccountNonLocked();   // (5)
	boolean isCredentialsNonExpired(); // (6)
	boolean isEnabled();               // (7)
}

UserDetails 인터페이스 코드이다

UserDetails인터페이스는 사용자의 권한정보 1), 패스워드2),Username3),을 포함하고 있으며 상요자 계쩡의 만료여부4) 사용자 계정의 lock 여부5), Credentials(Password)의 만료여부6), 사용자의 활성화 여부7)에 대한 정보를 포함하고 있다

 

UserDetailsService

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

public interface UserDetailsService {
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

UserDetailsService에서 확인 할수 있는것 처럼 UserDetailsService는 loadUserByUsername(String username) 메서드 하나만 정의하고 있으며 UserDetailsService를 구현하는 클래스는 loadUserByUsername(String username)을 통해 사용자의 정보를 로드한다

사용자의 정보를 어디에서 로드하는지는 애플리케이션에서 사용자의 정보를 어디서 관리하고 있느지에 따라 달라진다 즉, 사용자의 정보를 메모리에서 로드하든 데이터베이스에서 로드하든 Spring Security가 이해할 수 있는 UserDetails로 리턴해주기만 하면된다.

 

SecurityContext와 SecurityContextHolder

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

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

SecurityContext와 SecurityContextHolder의 구조 + 

SecurityContext가 인증된 Authentication을 포함하고 있고, 이 SecurityContext를 다시 SecurityContextHolder가 포함하고있다.

※SecurityContextholder가 SecurityContext를 포함하고 있는 것은 SecurityContextHolder를 통해 인증된 Authentication을 SecurityContext에 설정할 수 있고 또한 SecurityContextHolder를 통해 인증된 Authentication 객체에 접근할 수 있다는 것을 의미한다

 

public class SecurityContextHolder {
  ...
  ...

  private static SecurityContextHolderStrategy strategy;  // (1)
  
  ...
  ...

  // (2)
	public static SecurityContext getContext() {
		return strategy.getContext();
	}

  ...
  ...

  // (3)
	public static void setContext(SecurityContext context) {
		strategy.setContext(context);
	}

  ...
  ...
}
SecurityContextHolder 클래스의 일부
  • 1)은 SecurityContextHolder에서 사용하는 전략을 의미하며, SecurityContextHolder 기본전략은 ThreadLocalSecurityContexHolderStrategy 이다
    이 전략은 현재 실행 쓰레드에 SecurityContext를 연결하기 위해 ThreadLocal을 사용하는 전략이다
  • 2)의 getContext()메서드를 통해 현재 실행 쓰레드에서 SecurityContext를 얻을 수 있다
  • 3)의 setContext()메서드는 현재 실행 쓰레드에 SecurityContext를 연결한다
    setContext()는 대부분 인증된 Authentication을 포함한 SecurityContext를 현재 실행 스레드에 연결하는데 사용된다.

ThreadLocal
ThreadLocal은 쓰레드 간에 공유되지 않는 쓰레드 고유의 로컬 변수 같은 영역을 말한다

 

Point

  • UsernamePasswordAuthenticationFilter 는 클라이언트로부터 전달 받은 Username과 Password를 Spring Security가 인증 프로세스에서 이용할 수 있도록 UsernamePasswordAuthenticationToken 을 생성한다.
  • AbstractAuthenticationProcessingFilter는 HTTP 기반의 인증 요청을 처리하지만 실질적인 인증 시도는 하위 클래스에 맡기고, 인증에 성공하면 인증된 사용자의 정보를 SecurityContext에 저장하는 역할을 한다.
  • Authentication은 Spring Security에서의 인증 자체를 표현하는 인터페이스이다.
  • AuthenticationManager는 이름 그대로 인증 처리를 총괄하는 매니저 역할을 하는 인터페이스이며, 인증을 위한 실질적인 관리는 AuthenticationManager를 구현하는 구현 클래스를 통해 이루어진다.
  • ProviderManager는 이름에서 유추할 수 있듯이 AuthenticationProvider를 관리하고, AuthenticationProvider에게 인증 처리를 위임하는 역할을 한다.
  • AuthenticationProvider는 AuthenticationManager로부터 인증 처리를 위임 받아 실질적인 인증 수행을 담당하는 컴포넌트이다.
  • UserDetails 는 데이터베이스 등의 저장소에 저장된 사용자의 Username과 사용자의 자격을 증명해주는 크리덴셜(Credential)인 Password 그리고 사용자의 권한 정보를 포함하는 컴포넌트이며, AuthenticationProvider 는 UserDetails를 이용해 자격 증명을 수행한다.
  • UserDetailsService는 UserDetails를 로드(load)하는 핵심 인터페이스이다.
  • SecurityContext는 인증된 Authentication 객체를 저장하는 컴포넌트이고, SecurityContextHolder는 SecurityContext를 관리하는 역할을 담당한다.
728x90

'TIL' 카테고리의 다른 글

[Spring Security][JWT]  (0) 2022.11.23
[Spring Security] [세션/토큰 방식]  (1) 2022.11.23
[Spring Security][인증 구성요소 이해]  (0) 2022.11.21
[Spring Security][Web요청 처리 흐름]  (0) 2022.11.20
[인증/보안][SQL Injection][CSRF]  (0) 2022.11.19