Project/TomorrowLand

JWT를 이용한 로그인 기능 구현을하며

kkkkkdddddhhhhh 2023. 12. 25. 00:39

우선 저번 팀프로젝트에서는 Spring Inteceptor와  Session을 이용해서 로그인 기능을 구현했지만 이번엔 Spring Security와 JWT를 사용해 인증과 인가를 부여하기로했다. 

 

JWT를 사용한 이유 

  1. 학습의 목적.
    • 경험해보지않은 기술을 직접 구현해보며 학습하기위해
    • 최근 Security 강의를 들었던 경험의 복습
  2. 인증 정보를 서버에 저장하지않고 토큰에 저장해 사용자가 가지고다니는 Sateless한 점.
  3. Signature 에 의한 보안성 

크게 3가지 이유로 인해 이번 프로젝트에서 인증 기능은 JWT를 사용하기로했다.

 

Spring Security에서 JWT의 흐름부터 파악하며 공부하기 시작했다.

 

  1. 클라이언트에 로그인 요청 
  2. REST API Server 필터에서 요청 헤더에 JWT가 존재하는지 검증 (존재하지않는다면 다음 필터로 넘어감.)
  3. 클라이언트 요청에 포함된 이메일,비밀번호를 이용해 인증 토큰을 생성
  4. 인증 토큰을 사용해 인증 객체 생성 
  5. 인증객체의 정보를 이용해 JWT토큰 생성 (사용자 이메일, 권한등을 인증객체에서 가져온다.)
  6. JWT토큰을 헤더에 담아 응답

위 순서대로 코드를 작성하진않았고 여러가지 블로그와 깃허브 리파지토리를 참고해 코드를 완성했다.

 


 1. 클라이언트에 로그인 요청 

 

우선 로그인DTO로 클라이언트 요청을 받는다. 

 

 2. REST API Server 필터에서 요청 헤더에 JWT가 존재하는지 검증 (존재하지않는다면 다음 필터로 넘어감.)

 

UsernamePasswordAuthenticationFilter 가 걸리기전에 JWT커스텀필터가먼저 걸리게 addFilterBefore() 설정

 

필터 구현은 OncePerRequestFilter를 상속해 구현하기로 했다.

OncePerRequestFilter 를 사용한 이유로는

보통 Filter를 상속해 구현시 Filter는 서블릿 컨테이너로 갈 때 한번, 그리고 클라이언트 응답을 할 때 두번 중복으로 걸린다.

JWT를 검증하는 필터가 중복해 두번 걸릴필요가 없기에 OncePerRequestFilter를 상속해 구현.

 

헤더에 JWT토큰이 존재하는지 만료되었는지 잘못된 토큰인지 검증한다.

 

토큰이 존재하지않는다면 토큰을 생성해주고

 

토큰이 존재한다면?

주어진 토큰으로 인증객체를 생성한 뒤 

SecurityContextHolder에 인증객체를 설정해 사용자 정보를 사용 할 수 있게된다. 

즉, 사용자가 로그인하지않아도 액세스 토큰이 유효할경우 시큐리티에서 인증객체를 생성하고 사용 할 수 있다 는 이야기

 

 

 3. 클라이언트 요청에 포함된 이메일,비밀번호를 이용해 인증 토큰을 생성

 4. 인증 토큰을 사용해 인증 객체 생성 

 

 

요청 데이터를 바탕으로 인증토큰을 생성하고 인증 객체를 생성할 때 

loadUserByUsername 를 구현해야 하므로 CustomUserDetailsService Bean을 생성해

Authentication 인증 객체를 생성에 성공했다.

 

여기서 다른 블로그에서 jwt를 구현한 코드를 보면 대부분 CustomUserDetailsService를 @Service 패키지로 따로 빼둬 클래스로 구현했는데 

 

현재 내 생각으로는 시큐리티의 인증 프로바이더를 구현하는 클래스가 비지니스 레이어에 존재하는게 어울리지않다고 생각해 

SecurityConfig 에 @Bean을 생성해 구현했다.

현재 내 수준에서는 이게 맞다고 생각들지만 더 공부하고 다른 블로그 글이 적합하다 싶으면 맞게 리팩토링할 예정

 

 

 5. 인증객체의 정보를 이용해 JWT토큰 생성 (사용자 이메일, 권한등을 인증객체에서 가져온다.)

 

 

Subject에는 토큰을 제공하는 정보의 주체 

즉 토큰을 들고다닐 사용자의 유니크한 데이터를 담는다. 

 

나의 개인프로젝트에서 이메일이 곧 아이디이자 유니크 데이터이기때문에 Subject에는 이메일을 담았고,

 

Claim에는 주로 토큰에 정보를 담는다하여 권한 정보를 담아뒀다. 

 

액세스 토큰의 만료시간은 5분으로 설정했고, 리프레쉬토큰의 경우 14일을 줬다. 

 

 

 6. JWT토큰을 헤더에 담아 응답

 

 

HTTP 상태코드, 헤더에 액세스 & 리프레쉬 토큰, 바디에 json형태로 token 정보를 응답해줌으로 마무리

 

 

포스트맨으로 테스트해보자

 

 

body와 header 둘 다 토큰이 잘 응답되어있는것을 확인할 수 있다.

 

아직 JWT에대해 100%이해한건 아니지만...

확실히 인증정보(사용자 이메일)을 토큰으로 사용하다보니 세션을 이용할때보다 서버에서 처리할 일이 적어진거같아 가벼움이 장점인것같다.

 

아직 테스트 코드도 미구현되었고 

인증 인가에 대해 더 구현해야할게 많기때문에 이번 정리는 여기까지