JWT를 이용한 로그인 기능

Jongwon·2023년 2월 24일
1

DMS

목록 보기
7/18

앞서 ID와 패스워드값을 이용하여 UsernamePasswordAuthenticationToken을 발급받은 후, 이를 이용하여 JWT 토큰을 발급받도록 구현을 했었습니다. 하지만, 이후 소셜 로그인을 구현하면서 깨달았던 것이 로그인이 진행되는 과정을 정확히 이해하지 못하고 진행하니 패스워드 입력이 없는 소셜로그인의 JWT 발급을 어떻게 진행할지 감이 잡히지 않아 고생했었습니다.

이번 글에서는 ID와 PW(인코딩된)가 저장된 Member 데이터를 로그인하고자 할 때 거치는 과정을 디버깅하여 살펴보겠습니다.




Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);


TokenServicecreateToken() 메서드에 있는 문장입니다. 이 문장 한줄로 UsernamePasswordAuthenticationToken을 제공한 LoginRequestDTO는 Token 발급을 허가받습니다.

하지만 해당 문장은 Spring Security가 제공하는 여러 Filter와 Authentication을 통과합니다.


1. ProviderManager

authenticate()라는 메서드는 ProviderManager가 구현하고 있습니다.

authenticate() 메서드에서 for문을 통해 스프링빈에 등록된 DaoAuthenticationProvider를 찾습니다.(해당 객체를 provider변수에 저장)

그 뒤에 try문에서 DaoAuthenticationProviderauthenticate()메서드를 호출합니다. 여기에서 매개변수로 주는 값은 LoginRequestDTO에서 만들었던 UsernamePasswordAuthenticationToken입니다.

앞서 프로젝트를 따라오지 않으셨던 분들을 위해 설명드리자면 현재 Token에는 아래의 정보들이 담겨있습니다.

UsernamePasswordAuthenticationToken

  • principal : tank3a (ID값)
  • credentials : 1235abcd (PW값) //정상적인 패스워드는 1234abcd이지만 틀린 값을 주어 인증 과정을 살펴보고 있습니다.
  • authorities : empty list
  • details : null
  • authenticated : false

하지만 DaoAuthenticationProvider에 가면 authenticate()라는 메서드가 존재하지 않는데, 이는 상속해주는 Provider인 AbstractUserDetailsAuthenticationProvider가 이미 구현하였기에 이를 사용합니다.

authenticate() 메서드의 user는 MemberDetailsService를 구현한 서비스 계층에 있는 loadUserByUserName() 메서드에서 가져온 UserDetails객체입니다.

DB에는 [tank3a, 1234abcd]의 ID와 PW를 가진 데이터가 존재하기 때문에 ID값에 대해 User를 가져오는 if문은 정상적으로 통과를 했습니다.



비밀번호 체크는 addtionalAuthenticationChecks()메서드에서 진행하는데, 이 메서드는 DaoAuthenticationProvider에서 구현하고 있습니다.

👍 Deprecated된 이유를 정확히 확인할 수는 없었지만 인증된 사용자에 대해 또 다시 인증을 진행해 getPreAuthenticationChecks()와 getPostAuthenticationChecks()의 이용을 권장하기 때문이라고 추측됩니다. 하지만 Spring Boot 최신버전에서도 이 메서드를 사용하고 있기 때문에 크게 신경쓰지 않아도 될 듯 합니다.

앞서 언급했듯이 credentials에 비밀번호가 존재하고 있기 때문에 presentedPassword에 해당 값인 1235abcd(잘못된 password값)이 저장되어 있습니다.

따라서 아래의 BadCredentialsException()이라는 예외처리를 진행하는데, 이는 ProviderManager로 다시 돌아가면서 아래의 catch문에 걸립니다.

이후 parentProvider가 존재하면 해당 Provider도 통과하면서 인증 가능성을 확인하는데, 현재는 해당사항이 없으므로 가장 마지막의 코드인 아래의 코드로 갑니다.



prepareException()은 아래의 메서드로 AuthenticationEventPublisher의 Failure 메서드를 호출합니다.

스프링빈에 등록된 EventPublisher는 DefaultAuthenticationEventPublisher로, 아래의 publishAuthenticationFailure()메서드에 접근합니다.

파라미터로 전달된 값에 대한 설명입니다.

  • exception : 예외명과 예외 메세지
  • authentication : 예외가 발생한 인증(tank3a, 1235abcd), 즉 잘못된 authentication값

여기까지 진행된 후 예외를 가지고 authentication을 통과하지 못해 SecurityConfig의 AuthenticationEntryPoint에서 걸리고, 해당 핸들러 처리를 한 뒤 종료됩니다.

profile
Backend Engineer

0개의 댓글