개요
사용자 인증, 인가를 위한 Spring Security에 이은 JWT에 대해서도 정리
1. 토큰 기반 인증
오늘 정리할 JWT는 토큰 기반 인증 방식 중에 하나이다.
토큰 기반 인증이 무엇이냐, 사용자가 인증을 요청하면, 서버가 인증을 확인한 후 토큰을 발급해 클라이언트에 전달하는 방식이다.
이후에 클라이언트는 서버에 요청할 때마다 토큰을 사용해 인증을 증명한다.
위에 그림에 과정이 나와있고, 각 과정에 대해 자세하게 설명해보면
1) 사용자 인증 요청
-> 클라이언트에서 서버에 인증 정보를 보낸다 (사용자 ID, 비밀번호 등)
2) 서버 인증 확인 및 토큰 발급
-> 서버는 전달받은 정보를 확인하고 사용자가 올바른 사용자임을 인증한다.
-> 그 뒤, 클라이언트에 토큰(Token)을 발급한다.
-> 이때, 토큰은 사용자 정보와 인증된 증명을 포함하고, 암호화 또는 서명되어 변조를 방지한다.
3) 클라이언트 토큰 저장 및 요청
-> 클라이언트는 토큰을 저장하고 이후 요청 마다, 헤더 또는 URL 파라미터로 토큰을 포함해 서버에 요청을 보낸다.
4) 서버 토큰 검증 및 응답
-> 서버에서 토큰 유효성을 확인하고, 유효한 경우 요청을 처리한다.
1-1. 특징
무상태성(Stateless)
-> 토큰이 서버가 아닌 클라이언트에 있기 때문에 서버에 저장할 필요가 없다.
-> 즉, 서버 측에 인증 상태를 저장하지 않기 때문에 완전한 무상태로 효율적인 검증을 할 수 있다.
확장성(Scalability)
-> 서버 확장에 있어서 무상태성 특징을 가지고 있기 때문에 상태 관리를 신경 쓸 필요가 없다.
무결성
-> 토큰 발급 이후 토큰 정보를 변경하는 행위를 할 수 없어 무결성이 보장된다.
2. JWT(JSON Web Token)란?
JWT(JSON Web Token)는 사용자 인증 및 정보 교환을 위해 JSON 포맷으로 정보를 저장하고 안전하게 전송하기 위한 토큰이다.
2-1. 구조
Header
-> JWT 메타데이터를 담고 있고, 일반적으로 두 가지 정보가 포함된다.
{
"alg": "HS256",
"typ": "JWT"
}
: alg
-> 서명에 사용할 알고리즘 (HS256, RS256)
: typ
-> 토큰 타입 (일반적으로는 JWT)
Payload
-> 실제로 담고 싶은 데이터를 담는 부분으로 클레임(Claims)라고도 부른다.
-> 일반적으로 사용자 정보 및 권한 정보 등을 포함한다.
{
"userId": "1234567890",
"name": "Ho",
"admin": true
}
클레임에는 종류가 있는데
1) 등록된 클레임
-> 토큰의 유효성을 확인하기 위해 표준으로 정의된 클레임이다.
이름 | 설명 |
iss | 토큰 발급자 |
sub | 토큰 제목 |
aud | 토큰 대상자. 서비스 이름이나 사용자 ID |
exp | 토큰 만료 시간 |
nbf | 토큰 활성 날짜 |
iat | 토큰이 발급된 시간 |
jti | JWT 고유 식별자로 일회용 토큰에 사용한다. |
{
"iss": "https://example.com",
"sub": "user123",
"aud": "https://example-app.com",
"exp": 1710000000,
"nbf": 1700000000,
"iat": 1705000000,
"jti": "abc123def456ghi789"
}
2) 공개 클레임
-> 사용자가 정의해서 사용할 수 있는 클레임이다.
-> 단, 충돌을 방지하기 위해 URI 형식으로 네임스페이스를 사용해 정의하는 것이 좋다.
{
"https://example.com/roles": ["admin", "user"],
"https://example.com/email": "user@example.com"
}
3) 비공개 클레임
-> 공개적으로 정의되지 않은, 특정 애플리케이션이나 서비스 간에만 사용하는 클레임이다.
-> 클라, 서버 합의 하에 자유롭게 정의할 수 있다.
{
"userId": "user123",
"isPremiumUser": true,
"permissions": ["read", "write", "delete"]
}
Signature
-> 토큰의 무결성을 보장하기 위한 값으로, 헤더와 페이로드를 특정 비밀 키로 해싱하여 생성된다.
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload), secret
)
2-2. 특징
무상태, 확장성, 무결성의 경우는 JWT가 토큰 인증 방식이기 때문에 가질 수 있는 특징이다.
여기서 특징은 주의할 점에 대한 내용이다.
1) 보안 위험
-> 서명된 토큰은 누구나 열어볼 수 있기 때문에, 민감한 정보를 Payload에 넣는 것에 주의해야 한다.
2) 토큰 길이 문제
-> 복잡한 정보가 많이 포함되면 토큰의 길이가 길어지고, 네트워크 대역폭을 소모하게 된다.
3) 토큰 무효화 문제
-> 한번 발급된 토큰은 만료될 때까지 유효하기 때문에 만료 시간을 잘 설정하고 필요시 Refresh Token 전략을 사용한다.
2-3. Refresh Token을 활용한 JWT 토큰 과정
1) 인증 요청 (클라 -> 서버)
-> 클라이언트가 사용자 인증 정보(사용자 ID, 비밀번호)를 서버로 전달한다.
2) 토큰 생성 후 응답 (서버 -> 클라이언트)
-> 서버는 사용자 인증 정보를 검증하고, 인증이 성공하면 액세스 토큰과 리프레시 토큰을 발급한다.
{
"accessToken": "<Access Token>",
"refreshToken": "<Refresh Token>"
}
3) 리프레시 토큰 저장 (서버 -> DB)
-> 서버는 발급된 리프레시 토큰을 DB에 저장한다.
4) 토큰(만료) 정보와 함께 요청 (클라이언트 -> 서버)
-> 클라이언트는 요청 시마다 액세스 토큰을 포함해 요청을 보낸다.
-> 이때, 토큰이 만료된 경우 서버는 유효하지 않음을 응답한다.
GET /api/protected-resource
Authorization: Bearer <Access Token>
5) 토큰 검증 (서버)
-> 서버는 액세스 토큰을 검증한다.
-> 토큰의 유효 기간, 서명 등을 확인한다.
-> 만료 및 유효하지 않은 경우, 401 Unauthroized 에러를 반환한다.
HTTP/1.1 401 Unauthorized
{
"error": "TokenExpiredError",
"message": "Access token has expired."
}
6) 토큰 만료 응답 (서버 -> 클라이언트)
-> 서버는 액세스 토큰이 만료되었음을 클라이언트에게 알린다.
-> 클라이언트는 응답을 확인하고, 저장된 리프레시 토큰을 사용해 새로운 액세스 토큰을 요청한다.
7) 리프레시 토큰으로 액세스 토큰 발급 요청 (클라 -> 서버)
-> 서버로부터 받은 리프레시 토큰을 사용해 새로운 액세스 토큰 발급을 요청한다.
8) 리프레시 토큰 조회 및 유효성 검사
-> 서버는 DB에 저장된 리프레시 토큰을 조회하고 유효성을 검사한다.
9) 새로운 액세스 토큰 응답 (서버 -> 클라)
-> 리프레시 토큰이 유효하면, 새로운 액세스 토큰을 발급해 클라이언트로 반환한다.
-> 기존의 리프레시 토큰을 무효화하고, 새로운 리프레시 토큰을 발급하는 방식(Sliding Session 방식)을 적용할 수 있다.
이상 JWT에 대해 간단하게 정리 끝냈고, 다음 글에서 Security + JWT로 회원가입 및 로그인에 대해 쭉쭉 정리해보겠다.
'Back-end > Server' 카테고리의 다른 글
[Spring] HTTP 응답 Message Body 직접 입력 (2) | 2025.04.12 |
---|---|
[Spring] Spring Security 정리 (0) | 2025.04.06 |
[Spring] 스프링 구조 정리 (0) | 2025.01.11 |
[Server] 웹서버 VS 웹 어플리케이션 서버(WAS) (0) | 2024.06.02 |
[Spring] 스프링 빈이란? (0) | 2024.05.28 |