Spring Security

[Spring Security 6.x] FormLogin 동작 방식

person456 2024. 6. 15. 00:03

Spring Security에서 기본적으로 제공하는 인증 방법은 FormLogin 방식과 HttpBasic 요청 2가지가 존재한다.

 

목차

  1. formLogin()
  2. UsernamePasswordAuthenticationFilter

1. formLogin()

 

formLogin은 Http 기반의 폼 로그인 기반 인증을 실행해주는 API로, 사용자 인증을 위한 로그인 페이지, 로그아웃 페이지, 로그인 기능등을 제공해준다.

이러한 제공사항들을 토대로 사용자는 username과 password를 입력함으로써 서버로부터 인증을 수행받는다.

 

1-1 formLogin()의 API

method 이름 parameter 기능
loginPage String 로그인페이지의 URL을 지정.
해당 URL을 지정하면 Security가 제공하는 기본 로그인
페이지는 무시
loginProcessingUrl String 사용자 이름과 비밀번호를 검증할 URL 지정
(Form의 기준에서는 Action)
defaultSuccessUrl String, [boolean] 로그인 성공 이후 이동할 페이지
boolean이 true면 항상 해당 페이지로 이동
failureUrl String 인증에 실패할 경우 이동할 페이지 URL을 지정
기본값은 "/login?error"
usernameParameter String 인증을 수행할 때 사용자의 아이디를 찾기 위해 확인하는
HTTP 매개변수 이름 설정
기본값은 "username"
passwordParameter String 인증을 수행할 때 사용자의 비밀번호를 찾기 위해 확인하는
HTTP 매개변수 이름 설정
기본값은 "password"
failureHandler AuthenticationFailureHandler(
   HttpServletRequset
   HttpServletResponse
   AuthenticationException
)
인증 실패 때 사용할 사용자 정의 Handler를 구현할 수 있음.
기본값은 SimpleAuthenticationFailureHandler를 사용하여
"/login?error"로 이동
successHandler AuthenticationSuccessHandler(
   HttpServletRequest
   HttpServletResponse
   Authentication
)
인증 성공 때 사용할 사용자 정의 Handler를 구현할 수 있음.
기본값은 SavedRequestAwareAuthenticationSuccessHandler

 

formLogin 설정 예시

 

* defaultSuccessURL의 [boolean] 옵션

 

하지만 defaultSuccessUrl의 [boolean] 값이 true라면, defaultSuccessUrl의 우선순위가 더 높기 때문에 successHandler의 동작보다 먼저 default로 설정한 페이지로 이동을 실시한다.

 

* successHandler와 failureHandler

 

failureHandlersuccessHandler를 Security에서 제공하는 기본 설정값이 아닌, 설정에 의해 사용자가 재정의를 하는 경우 defaultSuccessUrl의 경로는 무시되고 사용자가 재정의한 Handler의 작업을 따른다.

 

 

* formLogin의 초기화 과정

처음 서버가 실행되면 HttpSecurity는 여러 설정을 진행한다.

그 중 formLogin은 FormLoginConfigurer 클래스를 통해 설정이 진행되는데, 코드는 다음과 같다.

 

HttpSecurity를 반환하는 formLogin을 살펴보면 Customizer를 통해 FormLoginConfigurer 클래스로부터 받은 값들을 토대로 설정을 진행한다.

FormLoginConfigurer는 위의 테이블에 설명한 내용들을 토대로 설정을 진행하고, 이 내용으로 HttpSecurity에 해당 설정을 저장하겠다는 의미다.

 

 

해당 FormLoginConfigurerAbstractAuthenticationFilterConfigurer의 상속을 받는데, 부모쪽에서 하는 일도 상당히 많다.

* AbstractAuthenticationFilterConfigurer

해당 클래스의 생성자를 살펴보면 defaultSuccessHandlerSaveRequestAwareAuthenticationSuccessHandler를,

loginPage는 "/login"으로 하는 모습을 볼 수 있다.

또한 Config 설정파일에서 주의깊게 봐야하는 메서드는 총 2가지로 init()과 configure() 메서드다.

 

 

가장 먼저 init메서드에서 설정하는 updateAuthenticationDefaults()를 살펴보자.

 

해당 메서드에서는 로그인 처리를 하는 Url과 failureHandler 설정, 그리고 Logout에 관한 설정을 하는 것을 볼 수 있다.

loginProcessingUrl이 설정되어있지 않다면 "/login"으로 설정을 해주고 있고,

failureHandler가 null이라면 "/login" url 뒤에 "?error"를 붙여 따로 작업을 진행해주고 있다.

또한 LogoutConfigurer를 통해 사용자가 따로 Logout에 관한 설정을 해주지 않는다면 로그아웃이 성공했을 때의 설정을 해주고 있는 모습을 볼 수 있다.

 

즉, updateAuthenticationDefaults()에서는

  1. loginProcessingUrl 기본 설정
  2. failureHandler 기본 설정
  3. Logout 성공 시의 기본 설정

이 3가지에 대한 기본 설정을 담당해주고 있다.

 

다음은 updateAccessDefaults()다.

해당 설정에서는 PermitAllSupport를 통해 permitAll에 관한 설정을 해주고 있다.

이는 즉 loginPage, loginProcessingUrl, failureUrl에 대해 permitAll 설정을 해줌으로써 인증받지 못한 경우에도 해당 Url은

접근이 가능하도록 기본 설정으로 해준다는 뜻이다.

 

 

 

2. UsernamePasswordAuthenticationFilter

 

사용자가 로그인을 진행하면 해당 인증 정보(아이디, 패스워드)를 토대로 인증을 진행할 필터가 필요하다.

AbstractAuthenticationProcessingFilter는 스프링 시큐리티에서 사용자의 인증을 진행하는 가장 기본적인 필터다.

Spring Security에서는 기본적으로 추상클래스인 AbstractAuthenticationProcessingFilter를 기반으로 아래의 그림과 같은 모습이 만들어진다.

 

 

 

AbstractAuthenticationProcessingFilter에는 attemptAuthentication() 메서드가 추상 메서드로 선언되어있다.

따라서 로그인에서의 인증 기능을 담당하기 위해서는 UsernamePasswordAuthenticationFilter에서 정의된

attemptAuthentication() 메서드가 사용되거나 사용자가 재정의한 클래스에서 해당 메서드를 재정의 해야한다.

UsernamePasswordAuthenticationFilter의 attempAuthentication() 메서드

 

해당 내용을 보면 HttpServletRequset로부터 사용자의 username, password를 토대로 UsernamePasswordAuthenticationToken을 생성하고, AuthenticationManager를 통해 인증을 진행한다.

 

 

AbstractAuthenticationProcessingFilter의 doFilter의 메서드를 살펴보면 우선 requiresAuthentication()메서드를 통해

사용자가 현재 보낸 요청이 해당 필터가 처리하는 내용이 맞는지 아닌지 여부를 파악한다.

만약 해당 필터가 처리할 내용이 아니면, chain.doFilter를 통해 다음 필터에게 일을 넘긴다.

 

 

requiresAuthentication() 메서드를 살펴보면 패턴으로는 "/login", HttpMethod는 "POST" 방식이라면 해당 필터가 일을 처리하는 것으로 기본 설정이 되어있다.

따라서 Spring Security가 기본적으로 제공하는 로그인 페이지에서 로그인을 진행했다면, 해당 필터에서 일을 처리하도록 설정되어 있는 것이다.

 

그렇다면 해당 if문은 빠져나와 else문으로 이동할 것이다.

 

여기서는 attemptAuthentication() 메서드를 통해 사용자가 입력한 아이디와 비밀번호를 기반으로 Token을 생성하고,

AuthenticationManager에게 인증 역할을 위임한다.

이후는 위의 흐름도와 같이 SecurityContextHolder에 UsernamePasswordAuthenticationToken을 저장하고, 해당 SecurityContext를 HttpSession에 저장하여 상태를 저장한다.