Spring boot

JWT (JSON Web Token) - 이론

devyelin 2025. 9. 23. 10:05

1. 토큰 이론

  • 사용자 인증 확인 방법에는 두가지가 존재
    • 서버 기반 인증 (세션 인증): Spring Security 기본 제공
    • 토큰 기반 인증
  • 토큰 기반 인증 특징
    1. 무상태성
      • 서버에서 인증 정보(토큰)을 저장할 필요 X (서버에 데이터 유지는 자원 소비가 큼)
      • 상태 관리: 클라이언트에서 사용자의 인증 상태를 유지하며 이후 요청 처리
      • 서버 입장: Client의 정보를 저장/유지 X  -> 완전한 무상태(Stateless)
    2. 확장성
      • 무상태성의 영향  ->  서버 확장 시 상태관리 신경 X  ->   서버 확장 용이
      • 세션 기반: 각각의 API에서 인증
      • 토큰 기반: 하나의 토큰으로 요청(Request)을 보냄
      • 추가: 페이스북, 구글 등 토큰 기반 인증을 사용하는 다른 시스템에 접근 가능
        이를 활용해 다른 서비스에 권한 공유 가능
    3. 무결성
      • 토큰 발급 후 변경 X  ->  토큰의 무결성 보장

  • Client 와  Server간의 토큰 교환
    1. (Client) 로그인 요청(Request): ID/PW를 서버에 전달하며 인증 요구
    2. (Server) 토큰 생성 및 응답(Response): 유효한 사용자(DB)인지 확인
    3. (Client) 토큰 저장: 서버에서 받은 토큰 저장
    4. (Client) 토큰 정보 + 요청(Request): 인증이 필요한 API에 접근할 때 토큰을 함께 전송
    5. (Server) 토큰 검증: 발급된 토큰이 유효한 지 확인
    6. (Server) 응답(Response): 유효한 경우 로직 처리 후 응답

2. JWT

  • 서버가 서명한 목적 / 주장(Claims) 의 봉투
  • HTTP Request(요청)의 헤더 중 Authorization 키 값에 "Bearer + JWT 토큰 값"을 넣어 전달하는 방식
  • 구조  (header.payload.signature): .을 기준으로 헤더, 내용, 서명으로 나뉨 
    • Header: 토큰의 타입(typ)와 해싱 알고리즘 정보(alg)
    • payload(Claims): 토큰과 관련된 정보, 내용의 한 덩어리(Claim) => Claim은 키값 한 쌍
      • Claim의 종류
        1. 등록된 Claim: 토큰에 대한 정보
          더보기
          "iss": 토큰 발급자
          "sub": 토큰 제목
          "aud": 토큰 대상자
          "exp": 만료 시간, NumbericDate형식, 현재 시간 이후로 설정
          "nbf": 활성 날짜 개념, NumbericDate형식, 이 날짜 지나기 전까지 토큰 처리 X
          "iat": 발급된 시간
          "jti": 고유 식별자, 일회용 토큰
        2. 공개 Claim: 공개되어도 됨, 충돌 방지 가능한 이름(보통 URI로 지음)
        3. 비공개 Claim: 공개 되면 안됨, Client - Server 간 통신에 사용
    • Signature: 조작/변경 확인하는 용도
      • 헤더의 인코딩 값 + 내용의 인코딩 값  =>  주어진 비밀 키를 사용해 해시값 생성

3. 토큰의 문제점

  • (문제 1) 토큰 자체가 노출되어 토큰을 탈취한 Request인지 확인 할 수 없음
    •  (해결 1) 토큰에 유효기간(exp)를 적용
  • (문제 2) 토큰의 유효기간이 너무 긴 경우: 그 기간동안 탈취자가 사용 가능
    • (해결 2) 토큰의 유효기간을 짧게 적용
  • (문제 3) 사용자의 입장에서 토큰의 유효기간이 짧아 매번 재접속, 불편
    • (해결 3) 리프레시 토큰 이용

4. 액세스 토큰 vs 리프레시 토큰

  • 액세스 토큰(Access Token) != 리프레시 토큰(Refresh Token)
  • 리프레시 토큰: 액세스 토큰이 만료된 경우, 새로운 액세스 토큰을 발급하기 위한 토큰
    • 리프레시 토큰의 유효기간 ↑ && 액세스 토큰의 유효기간 ↓
      → 공격자가 액세스 토큰을 탈취해도 짧은 시간(유효기간)이 지난 후 사용 X (안전)
  • 사용 방법(권장)
    • 액세스 토큰: 요청(Request) 보낼 때 HTTP 헤더로 보냄
      • 헤더명:  Authorization: Bearer <access_token>
      • 클라이언트에서는 메모리에만 보관 권장(LocalStorage는 XSS 취약)
    • 리프레시 토큰: HttpOnly + Secure(+ SameSite) 쿠키로 저장
      • 브라우저가 자동으로 전송, JS로 접근 불가(탈취 난이도 낮아짐)

5. 리프레시 토큰 회전(Refresh Token Rotation)

  • jti: UUID v4(문자열) 또는 32바이트 랜덤(Base64)
  • 리프레시 토큰 발급 시, jti 를 서버 저장소(DB)에 저장
  • 새로 액세스 토큰 갱신 시, 들어온 리프레시 토큰의 jti가 서버 저장소에 존재해야 함(통과)
    • (통과) 저장된 jti를 삭제(소프트)하고, 새로운 리프레시 토큰의 jti를 저장
    • 서버 저장소에서 같은 리프레시 토큰이 두번 이상 쓰이면(즉, 이미 지워진 jti) 탈취 의심
      →  서버 저장소에 존재하는 해당 유저의 jti를 모두 무효화
  • 로그아웃: 해당 사용자의 리프레시 jti를 모두 삭제(소프트)
    →  더이상 액세스 토큰 갱신 불가
  • 저장공간(DB): 주요 필드는 jti(리프레시 토큰 식별자), username(유저이름), exp(만료시간), revoked(사용됨)

6. 액세스 토큰 & 리프레시 토큰 흐름

  1. (Client) 로그인 요청(Request): ID/PW를 body에 넣어 Server로 요청
  2. (Server) 회원 인증: DB에 저장되어 있는 회원인지 확인
  3. (Server) 액세스 토큰/리프레시 토큰 발급: 
    1. 액세스 토큰은 Client에게 body로 전달
    2. 리프레시 토큰은 서버 저장소에 정보 저장 후, 헤더로 HttpOnly로 전달
      →  이제 리프레시 토큰이 필요한 경우, Client에서 credentials:'include' 옵션으로 전달하면 됨
  4. (Client) 응답 결과 저장: Server로부터 발급받은 엑세스 토큰을 메모리에 저장
  5. (Client) JWT 토큰이 필요한 API를 탈 때 요청의 헤더에 액세스 토큰을 포함해서 전달
  6. (Server) 액세스 토큰 유효기간 확인: 유효기간을 확인함 <필터 이용 추천>
    1. 유효기간 내: 해당 API의 서비스 로직 실행
    2. 유효기간 만료: 상태값 401을 보내며 Client에게 리프레시 토큰을 요청함
  7. (Client) 401을 받은 경우: 리프레시 토큰을 받아 액세스 토큰을 재발급하는 API로 리프레시 토큰 전달
  8. (Server) 리프레시 토큰 확인: 서버 저장소(DB)에 받은 리프레시 토큰의 jti를 기준으로 revokedfalse인지 확인
    1. revoked가 false인 경우: 해당 서버 저장소의 리프레시 토큰을 무효화시키고, 새로운 액세스 토큰과 리프레시 토큰을 발급(반복)
    2. revoked가 true인 경우: 탈취 가능성 존재, 예외 처리
  9. (Server) 결과를 Client로 전달
  10. (Client) 새롭게 발급 받은 토큰으로 메모리 업데이트 & 원래 타려던 API로 다시 요청보냄(반복)

 

 

 

 

 

'Spring boot' 카테고리의 다른 글

JWT (JSON Web Token) - 실습  (0) 2025.09.24
[Spring Boot] TDD (Test-Driven Development)  (1) 2025.04.22