개발 일지

Spring Security 인증 동작 과정(2) - Filter 본문

Spring/Spring Security

Spring Security 인증 동작 과정(2) - Filter

junjun_ 2023. 2. 7. 18:11

저번 글에서 Spring Security가 무엇이고 인증에 대한 기본 동작에 대해서 살펴보았다.

Spring Security는 필터를 기반으로 동작한다고 했었는데, 이번글에서는 이 Filer에 대해 알아보고 실재 코드를 보면서 인등(로그인) 동작을 자세히 정리해 보려고 한다.

 

Filter

Filter란

  • 클라이언트 요청이 서블릿으로 가기 전에 먼저 처리할 수 있도록 톰캣(WAS)에서 지원해 주는 기능
  • 서블릿 컨테이너의 Filter는 Dispatch Survlet으로 가기 전에 먼저 적용된다.
  • Filter들은 여러 개가 연결되어 있어 Filter chain이라고도 불린다.
  • 모든 Request들은 Filter chain을 거쳐야지 Survlet에 도착하게 된다.

 

필터는 서블릿에서 제공하는 기법이다.

Servlet Container는 스프링 컨테이너에 등록된 빈을 인식할 수 없다. 그렇기 때문에

Spring Security는 사용하고자 하는 FilterChain들을 Servlet Container 기반의 필터 위에서 동작시키기 위해 DelegatingFilterProxy라는 클래스를 이용한다. DelegatingFilterProxy는 IOC 컨테이너에서 관리하는 빈이 아닌 표준 서블릿 필터를 구현하고 있으며 DelegatingFilterProxy에 대한 자세한 내용은 따로 정리하도록 하겠습니다.

 

중요한 건 정해진 순서대로 스프링 Security의 필터들이 모두 FilterChain에 엮이게 되고, 각자 로직을 수행하고 나면 filterChain.doFilter() 메서드를 호출해 다음 필터 클래스의 doFilter() 메서드를 실행시킨다는 것이다. ( 필터는 doFilter부터 실행된다)

 

 

SecurityFilter의 종류

  • form 로그인 요청의 경우 필터체인에 들어와서 UsernamePasswordAuthenticationFilter에서 처리됩니다

 

코드 살펴보기

시작은 doFilter메서드

  • filterChain.doFilter() 메서드로 호출하기 때문에 Filter은 doFiler부터 실행이 된다고 했습니다. 
  • UsernamePasswordAuthenticationFilter의 doFilter 메서드는 부모클래스인 AbstractAuthenticationProcessingFilter에서 상속받습니다.

1. requiresAuthentication() 메서드를 통한 해당 요청을 처리할 것인지 확인

더보기

RequestMatcher를 통해 이 HttpServletRequest가 매칭되는지 확인 후 맞을 경우 try문을 처리하고 아닐 경우 필터체인의 다음 필터로 넘긴다.

이때 인증(로그인) 로직은 UsernamePasswordAuthenticationFilter에 걸란다고 했었는데 아래 코드를 보면

[Post] /login으로 요청이 들어오면 해당 Filter에 걸리도록 설정이 되어있기 때문이다.

결과적으로 로그인 요청은 필터체인을 돌다가 UsernamePasswordAuthenticationFilter에서 걸리게 된다.

2. attemptAuthentication() 호출하여 Authentication 객체를 반환받는다 (인증 로직 실행)

(1) UsernamePasswordAuthenticationFilter.attemptAuthentication()

  • AbstractAuthenticationProcessingFilter의 추상메서드인 attemptAuthentication메서드를 구현합니다.

  • (1)  사용자가 입력했던 id, password 정보로 UsernamePasswordAuthenticationToken 만들기
  • (2)  AuthrnticationManager.authentication 메서드로 인증객체의 인증 위임
AuthrnticationManager은 authentication메서드를 제공하는 인터페이스이며 실질적으로 authentication메서드를 처리하는 클래스는 AuthrnticationManager의 구현체인 ProviderManager클래스입니다.

 

(2) ProviderManager.authenticaticate()

  •  authentication메서드를 제공하는 AuthrnticationManager의 구현체

 

(2-1) for문을 돌면서 가지고 있는 providers 중에 해당 인증객체를 처리할 수 있는 provider을 찾음

더보기

 UsernamePasswordAuthenticationToken을 처리하는 povider은 DaoAuthenticationProvider인 것을 확인할 수 있다

  • 각각의 provider들은 supports 메서드를 가지고 있음 
  • supports 메서드에서 해당 인증객체를 처리할 수 있는지 확인한다.
  • DaoAuthenticationProvider의 부모클래스인 AbstractUserDetailsAuthenticationProvider클래스의 supports 메서드
  • UsernamePasswordAuthenticationToken을 처리할 수 있는것을 알수있다

 

(2-2) 위에서 찾은 DaoAuthenticationProvider의 authentication메서드로 인증처리를 위임

  • 위에서 DaoAuthenticationProvider가 UsernamePasswordAuthenticationToken을 처리할 수 있다는 것을 확인했다. 
  • DaoAuthenticationProvider의 authentication메서드는 DaoAuthenticationProvider의 부모클래스인 AbstractUserDetailsAuthenticationProvider에서 상속받는다

 

(3) DaoAuthenticationProvider.authentication메서드

  • AbstractUserDetailsAuthenticationProvider에 구현되어 있는 메서드 상속받음

(3-1) UserDetail정보 받아옴(id검증): DaoAuthenticationProvider의 retrieveUser() 메서드 

더보기
DaoAuthenticationProvider의 retrieveUser메서드
  • UserDetailsService의 loadUserByUsername() 메서드 실행
  • UserDetailService에서 username정보로 UserDetail 정보를 가져온다 (id 검증)

 

UserDetailsService 인터페이스
  • UserDetailsSevice는 loadUserByUsername() 메서드를 제공해 주는 인터페이스
  • 보통 사용자가 구현해서 사용한다.

 

(3-2) 패스워드 검증: DaoAuthenticationProvider의 additionalAuthenticationChecks메서드

더보기
DaoAuthenticationProvider의 additionalAuthenticationChecks메서드
  • 위에서 받아온 UserDetail의 password정보와 사용자가 입력한 password정보를 비교 검증한다. (password검증)
  • 이 때 passwordEncoder을 사용한다.

 

(3-3) Authentication 객체생성: DaoAuthenticationProvider의 createSuccessAuthentication 메서드

더보기
  • 여기까지 인증객체에 대한 인증이 정상적으로 완료가 되면 새로운 인증 객체를 만들어서 반환해 준다.
DaoAuthenticationProvider의 createSuccessAuthentication 메서드

 

3. successfulAuthentication() 메서드 Authentication 객체를 SecurityContextHolder에 저장

  • 인증이 완료된 Authentication객체를 SecurityContextHolder에 저장한다

  • 모든 처리가 완료되면 등록된 핸들러를 호출합니다

 

추가로 알아볼 내용

AbstractAuthenticationProcessingFilter의 doFIlter 메서드에서 Authemtication객체의 인증을 완료 후 successfulAuthentication() 호출하기 전에 세션에 대한 처리? 를 하는 거 같은데 이 코드 부분은 조금 더 공부해 봐야겠습니다.

this.sessionStrategy.onAuthentication(authenticationResult, request, response);

 

 

여기까지가 인증에 대한 내용이었고 다음에는 인가에 대한 살펴보겠습니다.