카테고리 없음

Security 인증 절차 풀어보기

Mon Groy 2024. 9. 4. 10:34

기본적인 Filter 안에 Delegating Filter Proxy 라는 서블릿 필터가 있다

 

Security에서 제공하는 Filter들을 실행시키는 클래스는 Filter Chain Proxy인데,

이 Filter Chain Proxy를 Delegating Filter Proxy가 실행 시킨다

*Security Filter 들에 커스텀한 Filter를 넣을 수도 있다

 

즉, 로그인을 요청하면 필터들이 제일 먼저 반응하는데, 그 중에 Delegating Filter Proxy라는 것이 있어서

그 필터가 작동되면서 Filter Chain Proxy를 실행시키고,

이 Filter Chain Proxy가 Security 필터들을 작동시키는 것


 

Spring Security는 기본적으로 세션 쿠키 방식을 사용해서 인증 절차를 수행하는데,

그 인증을 맡는 Filter가 UsernamePasswordAuthenticationFilter 이다

이 필터는 AbstractAuthenticationProcessingFilter를 상속받고 있다

 

AbstractAuthenticationProcessingFilter 에는 doFilter() 와 attemptAuthentication() 메서드가 있으며

doFilter()는 일반 메서드, attemptAuthentication() 는 추상메서드 이다

 

따라서 UsernamePasswordAuthenticationFilter 는 attemptAuthentication()를 구현해 놓았고

doFilter()는 FilterChainProxy에서 Filter 목록을 호출할때 사용한다

 

즉, Delegating Filter Proxy 가 Security 차례인 것을 Filter Chain Proxy 에게 알려주면

Filter Chain Proxy 가 doFilter() 메서드를 실행하여 Filter 목록을 호출하게 되는 것


doFilter() 중간에 해당 클래스에서 가지고 있는 추상메서드 attemptAuthentication()가 실행되는데,

이 때 UsernamePasswordAuthenticationFilter 의 attemptAuthentication()가 실행된다

 

UsernamePasswordAuthenticationFilter 에 구현된 attemptAuthentication() 메서드는

로그인시 입력된 request에서 username 과 password를 추출하여 둘 다 null이 아닌 경우

UsernamePasswordAuthenticationToken 타입을 authRequest 라는 이름으로 생성한다

더보기

UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password);

그 후, setDetails(request, authRequest); 를 통해 Details를 설정하고

마지막에 this.getAuthenticationManager().authenticate(authRequest);를 반환한다

 

즉, attemptAuthentication()를 실행시키면 AuthenticationManager를 호출하여 authenticate() 메서드를 실행하는 것

 


UsernamePasswordAuthenticationToken 는 AbstractAuthenticationToken을 상속받는데,

AbstractAuthenticationToken 클래스는 Authentication을 상속받는다

*AbstractAuthenticationToken  필드로는 details, authenticatied=false, authorities 가 있다

 

즉, 생성된  UsernamePasswordAuthenticationToken

SecurityContextHolder의 SecurityContext에 담기는

그 Authentication인 것이다 (물론 인증이 끝난 후에 담김)

 


 UsernamePasswordAuthenticationToken 클래스는 principal 과 credentials를 필드로 가지고 있고,

이 필드들이 매개변수로 들어오면 생성할 수 있는 생성자가 두 가지가 있다

 

하나는 (Object principal, Object credentials)  만 받아서 생성되는 대신

super(null); 과 setAuthenticated(false); 로 설정을 하고

 

다른 하나는 (Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities)를 받아서

super(authorities); 과 super.setAuthenticated(true); 로 설정을 한다

 

즉, 앞서 살펴봤던 attemptAuthentication() 메서드에서 생성하던 authRequest는

첫 번째 생성자를 사용한 것으로, 아직 인증되지 않은 Authentication 객체인 것임을 알 수 있다


이제 세 칸 위에서 호출됐던 AuthenticationManager 를 알아보자

 

getAuthenticationManager() 메서드는

UsernamePasswordAuthenticationFilter 가 상속받고 있는 AbstractAuthenticationProcessingFilter 의 객체인 AuthenticationManager 를 불러내는 메서드이다

더보기

private AuthenticationManager   authenticationManager;

 

interface 인 AuthenticationManager 클래스는 AuthenticationProvider라는 클래스 객체를 관리한다

*AuthenticationProvider는 실제 인증 로직이 담긴 객체이다

메서드는 authenticate(Authentication authentication)하나만 가지고 있으며

이 메서드의 반환타입은 Authentication이라고 정의만 되어있다

 

즉, UsernamePasswordAuthenticationFilter 가 AuthenticationManager의 authenticate() 메서드를 발동시키면

AuthenticationManager 의 구현체인 ProviderManager 에 있는 authenticate()가 실행된다

그리고 그 결과로 Authentication객체를 반환한다


ProviderManager 의 authenticate() 메서드에는 

AuthenticationProvider들을 for문으로 돌리고 있다

 

인터페이스 AuthenticationProvider에 authenticate() 메서드가 정의(만) 되어 있는 것 뿐 아니라

supports() 라는 메서드도 정의(만) 되어있다

 

supports()는 ProviderManager 의 authenticate()메서드의 if(!provider.supports()) 에서 사용되고 있는데,

이 if문은 매개변수로 (Class<? extends Authentication> 타입) 을 받는다

이는 authenticate()의 매개변수로 들어온 Authentication 객체가

AuthenticationProvider 객체에서 사용하는 Authentication과 같은지를 확인하는 절차이다

*if문이 extends Authentication 타입을 매개변수로 받고 있어서

이것이 참인 경우 다음 매서드로 넘어가고, 그렇지 않을 경우 다음 AuthenticationProvider로 for문이 돌게 되기 때문이다

 

즉, AuthenticationProvider 마다 다른 Authentication을 가지고 있어서

authenticate() 매서드의 매개변수로 들어온 authentication과 맞는 객체를 가진 AuthenticationProvider 를 찾는 것이다

 


위에서 찾은 인터페이스 AuthenticationProvider 의 구현체 또한 각각 authenticate() 메서드가 정의되어 있고,

이 메서드가 ProviderManager 의 for문이 끝난 다음에 실행되는 메서드이다

 

 AuthenticationProvider 의 구현체의 예로 AbstractDetailsAuthenticationProvider 클래스가 있다

이 클래스가 바로 우리가 인증을 위해서 사용할 Authentication을 가지고 있는 클래스이다

 

AbstractDetailsAuthenticationProvider 의 authenticate() 메서드를 보면

매개변수로 들어온 Authentication에서 username을 추출한다

username 이 null이 아닐 경우, 캐시에서 찾아서 UserDetails 타입으로 건져내고

캐시에서 찾은 이 UserDetails가 DB에 동일한 것이 있는지 체크한다(검증)


더보기

만약 캐시가 발견되지 않으면 DB에 있던 UserDetails로 앞서 캐시에서 찾았던 UserDetails를 대체하여 앞선 내용을 똑같이 수행한다.

이때 대체하는 메서드로 retrieveUser() 메서드가 수행되는데,

AbstractDetailsAuthenticationProviderretrieveUser() 메서드는 추상메서드이므로

AbstractDetailsAuthenticationProvider  를 상속받은 DaoAuthenticationProvider 클래스에서 구현해 둔 retrieveUser() 메서드가 수행된다


" 캐시에서 찾은 이 UserDetails가 DB에 동일한 것이 있는지 체크한다(검증) "는 부분을 수행하는 메서드는

additionalAuthenticationChecks() 인데, 이 또한 DaoAuthenticationProvider 에 구현되어 있다


authenticate() 메서드의 결과로 createSuccessAuthentication(principalToReturn, authentication, user) 가 반환된다

*여기서 매개변수 user는 UserDetails 타입임

 

 

createSuccessAuthentication() 메서드는

UsernamePasswordAuthenticationToken 타입의 결과를 반환한다

 

6칸 앞에서  UsernamePasswordAuthenticationToken 생성자가 두 가지가 있다고 했는데,

이 중에서 두 번째인 Authorities까지 포함한 Token 이 반환된다

*user 에서 getAuthorities()로 추출한 Authorities가 들어가게 됨

 


이렇게 doFilter() 의 attemptAuthentication()가 실행된 결과로 UsernamePasswordAuthenticationToken 타입의 authResult가 만들어졌다

 

이후 doFilter()의 반환값은

successfulAuthentication(request, response, chain, authResult) 가 되며

 

만약 authResult가 만들어지지 못하면 (또는 예외가 발생하면)

unsuccessfulAuthentication(request, response, failed) 가 반환된다

 

successfulAuthentication() 메서드에는

authResult를 SecurityContext에 저장하는 코드가 들어있어서

이것으로 인증 절차가 끝나게 된다

 


참고

 

https://cjw-awdsd.tistory.com/45

 

[스프링] Spring Security 인증은 어떻게 이루어질까?

예전 포스팅에서 security 관련 예제를 다룬적 있다. 당시에는 내부 프로세스를 모르고 예제를 보면서 UserDetails, UserDetailsService, Authentication만 커스텀해서 인증을 다뤘다. 이번 토이프로젝트에서 OAu

cjw-awdsd.tistory.com

 

https://hohodu.tistory.com/entry/JWT-JWT-%EC%9D%B8%EC%A6%9D%EC%9D%B8%EA%B0%80-%EA%B5%AC%ED%98%84%EA%B8%B01-Spring-Security-%EC%9D%B8%EC%A6%9D-%EA%B5%AC%EC%A1%B0?category=1150624

 

[JWT] JWT 인증/인가 구현기(1) - Spring Security 인증 구조

목표 : JWT 인증/인가 과정을 구현 그 전에 Spring Security를 알아보자 들어가기 전에 Spring Security 스프링 시큐리티는 스프링 기반의 애플리케이션 보안(인증, 인가, 권한)을 담당하는 스프링 하위 프

hohodu.tistory.com


https://jaykaybaek.tistory.com/27

 

[Spring Security] UserDetailsService와 UserDetails 및 Authentication의 차이점

1. UserDetailsManager 이해하기스프링 시큐리티 필터를 거친 다음, AuthenticationManager는 적절한 Provider를 선택한 후 UserDetailsManager를 호출합니다.상속도전반적인 상속도입니다. 최하단의 3개의 클래스는

jaykaybaek.tistory.com

추후 추가하여 정리하면 좋을 포스트