본문 바로가기
dev/Spring-security

[Spring] Spring security - ApplicationFilterchain, DelegatingFilterProxy

by dev_Step 2022. 8. 20.

스택을 보면

FilterChainProxy -> do.Filter() -> (이동)해당필터.doFilter() -> 요청 ~~ 이동 쭉쭉 이어간다

이때 

FilterChainProxy 클래스에서  nextFilter.doFilter() 가 실행되는데 이때 nextFilter는 

additionalFilters에 담겨있는 N개의 필터중 currentPostition 순번째의 필터를 의미한다. 즉

currentPosition 이 addtitionalFilters.size() 와 동일할때 까지 등록되어 있는 SecurityFilterChain 이 다돌아갈떄 까지 진행된다고 생각하면된다. 

UsernamePasswordAuthenticationFilter.doFilter() 를 요청했는데

UsernamePasswordAuthenticationFilter 는 (AbstractAuthenticationProcessingFilter)를 상속하였으므로 AbstractAuthenticationProcessingFilter.doFiliter()가 실행되고, 해당 doFilter()안에서 

    - attemptAuthentication(request, response)  메서드가 실행되었고, 해당 attemptAuthentication(request, response)  는 

    - UsernamePasswordAuthenticationFilter 에서 재정의하고 있으므로 UsernamePasswordAuthenticationFilter 에서 실

     행된다.

 

 

<UsernamePasswordAuthenticationFilter> - attemptAuthentication()

 

해당 필터에서  UsernamePasswordAuthenticationToken을 만들어서  

UsernamePasswordAuthenticationToken 의 Details에 주입하고 있다.

  -> Token생성 후  SetDetails(token) 토큰 주입

 

return 값은  AuthenticationManager 의 authentication() 메서드를 실행하면서 생성한 Token을 주입하고 있다.

AbstractAuthenticationProcessingFilter <- UsernamePasswordAuthenticationFilter 

UsernamePasswordAuthenticationFilter 의 부모인 AbstractAuthenticationProcessingFilter 에 정의 되어있다.

 

즉 AuthenticationManager의  authenticate(Authentication authentication) 를 실행하는것인데 

AuthenticationManager 는 Interface로써, AuthenticationManager 를 구현하고 있는 ProviderManager가 실행된다.

ProviderManager.Authentication(Authentication auth)

코드는 아애와 같다.

public Authentication authenticate(Authentication authentication)
			throws AuthenticationException {
		Class<? extends Authentication> toTest = authentication.getClass();
		AuthenticationException lastException = null;
		Authentication result = null;
		boolean debug = logger.isDebugEnabled();

		for (AuthenticationProvider provider : getProviders()) {
			if (!provider.supports(toTest)) {
				continue;
			}

			if (debug) {
				logger.debug("Authentication attempt using "
						+ provider.getClass().getName());
			}

			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;
			}
			catch (InternalAuthenticationServiceException e) {
				prepareException(e, authentication);
				throw e;
			}
			catch (AuthenticationException e) {
				lastException = e;
			}
		}

		if (result == null && parent != null) {
			// Allow the parent to try.
			try {
				result = parent.authenticate(authentication);
			}
			catch (ProviderNotFoundException e) {
				// ignore as we will throw below if no other exception occurred prior to
				// calling parent and the parent
				// may throw ProviderNotFound even though a provider in the child already
				// handled the request
			}
			catch (AuthenticationException e) {
				lastException = e;
			}
		}

		if (result != null) {
			if (eraseCredentialsAfterAuthentication
					&& (result instanceof CredentialsContainer)) {
				// Authentication is complete. Remove credentials and other secret data
				// from authentication
				((CredentialsContainer) result).eraseCredentials();
			}

			eventPublisher.publishAuthenticationSuccess(result);
			return result;
		}

		// Parent was null, or didn't authenticate (or throw an exception).

		if (lastException == null) {
			lastException = new ProviderNotFoundException(messages.getMessage(
					"ProviderManager.providerNotFound",
					new Object[] { toTest.getName() },
					"No AuthenticationProvider found for {0}"));
		}

		prepareException(lastException, authentication);

		throw lastException;
	}

 해당 코드를 실행하면서

-- result = parent.authenticate(authentication) 에서 provider.aithenticate() 코드로 이동되는데 이때 

-- provider는 

-- for( AuthenticationProvide provider : getProviders()){} 매서드를 통해서 얻은것중 하나로

--  사용자 정의 DB를 통해서 권한을 얻을때 사용되는 AuthenticationProvider로는 DaoAuthenticationProvider 이다.

-- DaoAuthenticationProvider는 AbstractUserDetailsAuthenticationProvider를 상속한다.

따라서 DaoAuthenticationProvider.authentication(authentication) 의경우는 부모의 클래스에서 정의된.

 - AbstractUserDetailsAuthenticationProvider.authentication() 이 실행되고 해당 메서드 안에서 

  - 정의된 retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication); 를 통해서 로그인을 시도한 사람의 UserDetails를 얻어서 user 의 정보를 얻게 된다.

  - 그후 코드를 쭉 따라서 아래로 이동하면 createSuccessAuthentication() 매서드를 통해서 

  - 사용자의 UsernamePasswordAuthenticationToken이 생성되고,  해당 값을 return 해주는것을 알수있다.

 

* UsernamePasswordAuthenticationToken -> AbstractAuthenticationToken ->  Authentication

 위의 상속과정을 따르므로  UsernamePasswordAuthenticationToken을 리턴해도 Authentication으로 받을수 있다.

이렇게 Authentication이 리턴되면 

 SecurityContext에 Authentication 저장 후 인증 완료 처리하면서 로그인이 성공합니다.

 

============================================================================================