코딩공작소

Springboot3 실습 (4) - JWT로 로그인/로그아웃 구현하기 본문

어플리케이션개발/springboot실습

Springboot3 실습 (4) - JWT로 로그인/로그아웃 구현하기

안잡아모찌 2024. 4. 3. 21:49

토큰 기반 인증이란 무엇일까 ?

사용자 인증 확인 방법에는 서버 기반 인증과 토큰 기반 인증이 있다.
먼저, 지난 실습 (3) 글에서 정리한 내용이 세션 기반인증

토큰을 이용한 인증이 이번 장에서 설명한 토큰 기반 인증이다.

 

 

토큰 인증 과정

  • 클라이언트가 로그인요청을 서버로 보낸다
  • 서버는 유효성 검증을 하고, 통과하면 토큰을 생성하여 응답한다.
  • 클라이언트는 토큰을 저장한다.
  • 이후 인증이 필요한 API를 사용할 때 토큰을 함께 보낸다.
  • 서버는 토큰의 유효성을 검증한다.
  • 토큰이 유효하다면 클라이언트가 요청한 내용을 처리한다.

 

 

토큰 기반 인증의 특징

  • 무상태성 : 서버 입장에서 클라이언트의 인증 정보를 저장하거나 유지하지 않아도 되기 때문에 완전한 무상태으로 검증할 수 있다.
  • 확장성 : 하나의 토큰으로 결제 서버와 주문 서버에게 요청을 보낼 수 있는 다른 서비스에 권한을 공유할 수 있는 확정성이 있다.
  • 무결성 : 토큰 정보를 변경할 수 없는 무결성이 보장된다.

 

 


JWT

JWT는 "." 을 기준으로 헤더, 내용, 서명으로 이루어져 있다.

-> aaaaa(header) . bbbbb(payload) . cccccc(signature)

 

  1. 헤더 : 토큰의 타입과 해싱 알고리즘을 저장하는 정보를 답는다
    {
       "typ" : "JWT", ( 토큰의 타입을 지정한다. JWT라는 문자열이 들어간다. )
       "alg" : "HS256" ( 해싱 알고리즘을 지정 )
     }
  2. 내용 : 토큰과 관련된 정보를 담는다. 내용의 한 덩어리를 클레임이라 부르며, 이는 키값의 한 쌍으로 이루어져 있다.

이름 설명
iss 토큰 발급자(issuer)
sub 토큰 제목(subject)
aud 토큰 대상자(audience)
exp 토큰의 만료시간 , NumericDate 형식
nbf 활성 날짜와 비슷한 개념으로 Not Before를 의미하며 이 날짜가 지나기 전까지는 토큰이 처리되지 않는다.
iat 토큰이 발급된 시간
jti 고유 식별자로서 주로 일회용 토큰에 사용

 공개 클레임 : 공개 되어도 무방한 클레임. 보통 URI로 이름을 짓는다.
 비공개 클레임 : 공개되면 안되는 클레임.

 3. 서명 : 토큰이 조작/변경 되지 않았음을 확인하는 용도로, 헤더의 인코딩 값과 내용의 인코딩 값을 합친 후에 주어진 비밀키를 사용해 해시값을 생성한다.

 

 

토큰의 유효기간

사용자의 보안과 관련된 이슈에 대하여 토큰의 유효기간 때문에 리프레쉬 토큰이라는 것을 사용한다.
이는 액세스 토큰 발급 후, 추 후 발급되는 토큰이다.

  • 2 : 요청 검증 후 리프레시 토큰도 생성하여 클라이언트에 전달하고 디비에도 저장한다.
  • 그 후, 액세스 토큰이 만료되면 만료 응답을 보내고 클라이언트 에서는 리프레시 토큰과 함께 새로운 액세스 토큰을 요청한다.
  • 유효한 리프레시 토큰일 경우 새로운 액세스 토큰을 생성한 뒤 응답한다.

 

 

이론 끝


JWT 구현하기

 

의존성 추가

 

토큰 제공자 추가

이슈 발급자와 비밀키를 설정한다.

 

 

해당 값들을 변수로 접근하는 데 사용할 클래스를 만든다.

 

 

계속해서 토큰을 생성하고 올바른 토큰인지 유효성을 검사하고, 토큰에서 필요한 정보를 가져오는 클래스

 

 

validToken : 토큰이 유효한지 검증. 프러퍼티즈 파일에 선언한 비밀값과 함께 토큰 복호화를 진행
getAuthentication : 토큰을 받아 인증 정보를 담은 객체를 반환하는 메서드. 프로퍼티즈 파일에 저장한 비밀 값으로 토큰을 복호화한 뒤 클레임을 가져오는 getClaims()를 호출해서 클레임 정보를 반환받아 사용자 이메일이 들어 있는 토큰 제목 sub와 토큰 기반으로 인증 정보를 생성한다. 
getUserId : 프로퍼티즈 파일에 저장한 비밀값으로 토큰을 복호화한 다음 클레임을 가져오는 getClaims를 호출해서 클레임 정보를 반환받고 클레임에서 id 키로 저장된 값을 가져와 반환

 

빌더 패턴을 사용해 객체를 만들 때 테스트가 필요한 데이터만 선택. 필드 기본값을 사용.

 

 

테스트 코드

 

 

 

 

 

 

 

 

리프레시 토큰 도메인 구현

리프레시 토큰 도메인 생성

 

리포지토리 생성

 

 

토큰 필터 구현

토큰 필터는 요청 처리 전에 로직으로 전달되기 전후에 URL 패턴에 맞는 모든 요청을 처리하는 기능을 한다. 헤더값을 비교해서 토큰이 있는지 확인하고 유효 토큰이라면 시큐리티 콘텍스트 홀더(Security Text Holder)에 인증 정보를 저장한다.

시큐리티 컨텍스트는 인증 객체가 저장되는 보관소로, 인증 정보가 필요할 때 언제든지 인증 객체를 꺼내 사용한다. 이 클래스는 스레드마다 공간을 할당하는 스레드 로컬에 저장되므로 코드의 아무곳에서나 참조할 수 있고, 다른 스레드와 공유하지 않아서 독립적으로 사용할 수 있다.시큐리티 컨텍스트 객체를 저장하는 객체가 시큐리티 컨텍스트 홀더다.

 

 

토큰의 유효성 검사를 한 후, 유효하다면 인증 정보를 관리하는 시큐리티 컨텍스트에 인증정보를 설정
작성한 코드가 실행되며 인증 정보가 설정된 이후에 컨텍스트 홀더에서 getAuthentication() 메서드를 사용해 인증 정보를 가져오면 유저 객체가 반환
유저 객체에는 유저이름과 권한목록과 같은 인정 정보가 포함

 

 

토큰 API 구현하기

리프레시 토큰을 전달받아 검증하고, 유효한 리프레시 토큰이라면 새로운 액세스 토큰을 생성하는 토큰 API를 구현한다.

 

 

서비스 추가

UserService단에 유저 ID 검색 메서드를 추가해준다.

 

전달받은 리프레시 토큰으로 리프레시 토큰 객체를 검색해서 전달하는 메서드 구현

 

 

전달받은 리프레시 토큰으로 토큰 유효성 검사를 진행하고, 유효한 토큰일 때 리프레시 토큰으로 사용자 ID를 찾는 메서드
마지막으로, 사용자 ID로 사용자를 찾은 후 토큰 제공자의 generateToken()메서드를 호출해서 새로운 액세스 토큰을 생성한다.

 

 

토큰 생성 요청 및 응답을 담당할 DTO를 생성한다.

 

 

요청이 오면 토큰 서비스에서 리프레시 토큰을 기반으로 새로운 액세스 토큰을 만들어준다.

 

 

 

테스트 코드

 

 

토큰 기반 인증은 인증에 토큰을 사용하는 방식으로, 토큰은 클라이언트를 구분하는 데 사용하는 유일한 값으로서 서버에서 생성해서 클라이언트에게 제공한 뒤, 클라이언트는 서버에 요청할 때마다 요청 내용과 함께 토큰을 전송합니다. 서버에서는 토큰으로 유효한 사용자인지 검증합니다.

JWT는 토큰 기반 인증에서 주로 사용하는 토큰으로, JSON 형식으로 사용자의 정보를 저장한다. 헤더.내용.서명 구조로 이루어져 있으며, 헤더는 토큰의 타입과 해싱알고리즘, 정보에는 토큰을 담을 정보, 서명은 해당 토큰이 조작되었거나 변경되지 않았음을 확인하는 용도

리프레시 토큰은 액세스 토큰이 만료되었을 때 새로운 토큰 액세스 토큰을 발급받는 용도

필터는 실제로 요청이 전달되기 전/후에 URL 패턴에 맞는 모든 요청을 처리하는 기능을 제공

시큐리티 콘텍스트는 인증 객체가 저장되는 보관소로, 인증 정보가 필요할 때 언제든지 인증 객체를 꺼내어 사용하도록 제공되는 클래스로 시큐리티 컨텍스트를 저장하는 객체가 시큐리티 컨텍스트 홀더다.