[Spring Security] JWT (1)
JWT란 무엇인가
✏️ Cookie & Session
JWT를 이해하기 전에, 먼저 쿠키와 세션에 대해서 알아야 한다.
사용자가 애플리케이션을 사용할 때, 각 기능을 호출할 때마다 인증을 받는 것은 매우 귀찮을 것이다. HTTP는 Stateless 하기 때문에, 매 호출마다 인증을 받아야 한다.
위와 같은 문제점을 해결하기 위해 쿠키와 세션을 사용한다.
■ Cookie
쿠키란?
웹 브라우저에 저장되는 작은 데이터 조각
- 클라이언트 상태정보를 쿠키로 웹 브라우저에 저장해놓는다.
- 쿠키로 사용자 인증을 유지할 수 있다.
- 쿠키는 브라우저를 종료해도 없어지지 않는다!
- 만료시점이 지나면 자동으로 삭제된다.
쿠키는 다음과 같은 과정으로 작동한다.
- 클라이언트가 서버에 페이지를 요청
- 서버에서 쿠키 생성
- HTTP Header에 쿠키를 포함시켜서 응답
- 클라이언트는 웹 브라우저에 해당 쿠키를 보관
- 다음 요청부터는 쿠키를 넣어서 보냄
■ Session
세션이란?
서버에서 관리되는 사용자와의 대화 상태
세션은 쿠키를 기반으로 작동한다!
- 서버는 세션 ID를 쿠키에 넣어서 클라이언트에 전달한다.
- 웹 브라우저가 해당 세션 ID를 가지고 서버에 요청하면, 서버는 세션 ID를 통해 인증을 한다.
- 세션은 브라우저가 종료되면 삭제된다.
세션은 다음과 같은 과정으로 작동한다.
- 클라이언트가 서버에 페이지를 요청
- 서버에서 쿠키 생성 With 세션 ID
- HTTP Header에 쿠키를 포함시켜서 응답
- 클라이언트는 웹 브라우저에 해당 쿠키(세션 ID가 들어있음)를 보관
- 다음 요청부터는 쿠키를 넣어서 보냄
■ Cookie VS Session
가장 큰 차이점은, 사용자 정보의 저장 위치이다.
- Cookie: 사용자 정보가 브라우저(클라이언트)에 저장되어 있음
- Session: 사용자 정보가 서버에 저장되어 있음. 브라우저는 세션 ID만 쿠키 내에 갖고 있음
라이프 사이클도 다르다.
- Cookie: 만료 시점까지 삭제되지 않음
- Session: 브라우저 종료 시 삭제
✏️ Session의 한계
세션의 동작 과정을 더 자세히 알아보자.
Session ID 발급 과정
- 클라이언트가 서버에 페이지를 요청
- 서버는 세션 ID를 생성하고, 사용자 정보를 저장할 공간을 미리 만들어 둔다.
- 세션 ID가 포함된 쿠키를 Header에 넣어서 응답
- 클라이언트는 웹 브라우저에 세션 ID가 들어있는 쿠키를 보관
로그인 과정
- 클라이언트가 서버에 로그인 요청
- 서버는 사용자가 회원가입이 되어있는지 확인(인증)
- 인증된 사용자라면, 해당 사용자의 정보를 세션 ID와 함께 저장
- 응답 - 메인페이지로 이동
위의 두 과정을 거치면, 세션 ID만 있어도 인증을 받을 수 있다.
예를 들어, 사용자 정보를 요청하는 HTTP Request를 세션 ID와 함께 서버에 보내면, 서버는 세션 영역에서 해당 세션 ID에 맞는 사용자 정보를 찾는다.
■ 문제점
만약, 서버가 여러대라면?
수강신청 서버 A, B, C가 존재한다. 로드밸런싱을 사용해서 각 서버에 적절하게 부하를 분산시킨다고 가정해보자.
- 철수가 수강신청을 하기 위해 서버 B에 로그인 했다.
- 철수의 정보는 서버 B의 세션 영역에 저장된다.
- 로그인을 완료한 철수가 “수강신청” 버튼을 눌렀는데, 로드밸런싱에 의해 서버 A로 요청이 전송되었다.
- 서버 A의 세션 영역에는 철수의 세션 정보가 없기 때문에, 다시 로그인을 해야하는 상황이 발생한다!!
이러한 문제점을 해결하기 위해서 다음과 같은 방법들을 사용할 수 있다.
| 해결방법 | 기능 |
|---|---|
| Session Stickiness | 로드 밸런서가 특정 사용자의 요청을 항상 같은 서버로 전달하도록 설정 예를 들어, 철수가 서버 B에 로그인하면, 이후의 모든 요청은 서버 B로 전달된다. |
| 세션 정보 중앙 저장 | 세션 정보를 중앙 저장소에 저장하여 모든 서버가 동일한 세션 정보를 공유하도록 한다. - DB 중앙 저장소: 매 로그인마다 I/O가 필요해서 성능이 낮음 - 인메모리 중앙 저장소(Redis): DB 저장소보다 빠름 |
그리고, 해결 방법이 한 가지 더 있다. 바로 JWT를 사용하는 것이다.
✏️ JWT
JWT (JSON Web Token)
: JSON 형식의 정보를 사용하여 클라이언트와 서버 간에 안전하게 정보를 전송하는 데 사용되는 표준 웹 토큰
■ JWT의 구조
JWT는 점(.)으로 구분된 세 부분으로 구성된다.
| 구성요소 | 설명 |
|---|---|
| Header | 아래와 같은 정보를 포함한다. - 토큰의 유형: JWT - 사용된 해싱 알고리즘: ex) HMAC SHA256 |
| Payload | 토큰에 포함될 실제 데이터(claim) |
| Signature | Header와 Payload를 인코딩한 후 비밀키로 해싱한 값 |
위의 3가지 요소들을 Base64로 인코딩 하면 다음과 같은 결과가 나온다.
1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
이것이 바로 JWT 토큰이다.
■ 무결성
JWT는 서명(Signature)을 통해 무결성을 보장한다.
즉, 서명을 통해 토큰이 생성된 이후 변경되지 않았음을 보장한다.
- Header와 Payload를 Base64로 인코딩한 다음, 서버의 비밀키를 포함해서 HS256으로 암호화한다.
Signature = HS256(Base64(Header) + "." + Base64(Payload) + secret)- Header,Payload,Signature 를 각각 Base64로 인코딩한 결과를 합쳐서 JWT 토큰을 만든다.
클라이언트가 JWT 토큰을 보내면, 서버는 토큰을 생성할 때와 똑같은 방식으로 Signature를 만들어내고 JWT 토큰 내부에 존재하는 Signature와 비교한다.
■ 동작 방식
- 로그인: 사용자가 로그인을 하면 서버는 사용자의 인증 정보를 검증한 후 JWT를 생성하여 클라이언트에게 반환한다.
- 저장: 클라이언트는 받은 JWT를 로컬 저장소(ex: LocalStorage)에 저장.
- 요청: 클라이언트가 서버에 요청할 때 JWT를 HTTP 헤더에 포함시켜 전송.
- 검증: 서버는 클라이언트로부터 받은 JWT를 검증하여 유효성을 확인한 후 요청을 처리.
Spring Security를 활용하여 JWT를 구현하는 것은 다음 포스트에서 알아보자.

