본문 바로가기
spring

[Spring] JWT기반 로그인 예시

by goblin- 2024. 12. 11.

JWT 기반 로그인 과정의 구체적인 예시

 

아래는 JWT 기반 로그인 과정을 구체적인 데이터와 저장 방식을 포함하여 자세히 설명한 내용입니다. 회원가입부터 로그인, 토큰 저장 및 검증, API 요청까지의 흐름을 따라갑니다.

 

1. 회원가입

 

1.1 프론트엔드 (사용자 입력)

 

사용자 user1이 아래 정보를 입력:

{
  "username": "user1",
  "password": "password123",
  "email": "user1@example.com"
}

 

프론트엔드는 이를 백엔드에 POST 요청:

POST /api/register
Content-Type: application/json

{
  "username": "user1",
  "password": "password123",
  "email": "user1@example.com"
}

 

 

 

1.2 백엔드 (데이터 저장)

 

1. 비밀번호 암호화

password123을 암호화하여 데이터베이스에 저장:

String hashedPassword = passwordEncoder.encode("password123");

 

 

암호화된 비밀번호 예: $2a$10$GzQ...

 

2. 사용자 정보 데이터베이스 저장

데이터베이스 테이블:

CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) UNIQUE,
    password VARCHAR(100),
    email VARCHAR(100)
);

 

 

저장된 데이터:

id    username   password                              email
1     user1      $2a$10$GzQ...                        user1@example.com

 

 

 

2. 로그인

 

2.1 프론트엔드 (사용자 로그인 요청)

 

사용자 user1이 아래 정보를 입력:

{
  "username": "user1",
  "password": "password123"
}

 

 

프론트엔드는 이를 백엔드에 POST 요청:

POST /api/login
Content-Type: application/json

{
  "username": "user1",
  "password": "password123"
}

 

 

 

 

2.2 백엔드 (인증 및 토큰 생성)

 

1. 사용자 정보 조회 및 검증

데이터베이스에서 username = 'user1'인 사용자 검색:

id    username   password                              email
1     user1      $2a$10$GzQ...                        user1@example.com

 

 

 

입력한 비밀번호(password123)와 저장된 암호화된 비밀번호를 비교:

passwordEncoder.matches("password123", "$2a$10$GzQ...");

결과: 일치함.

 

2. Access Token 생성

JWT Access Token 생성:

String accessToken = Jwts.builder()
    .setSubject("1") // 사용자 ID
    .claim("username", "user1")
    .setIssuedAt(new Date())
    .setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1시간
    .signWith(SignatureAlgorithm.HS256, secretKey)
    .compact();

 

 

생성된 Access Token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

 

 

3. Refresh Token 생성

JWT Refresh Token 생성:

String refreshToken = Jwts.builder()
    .setSubject("1") // 사용자 ID
    .setIssuedAt(new Date())
    .setExpiration(new Date(System.currentTimeMillis() + 604800000)) // 7일
    .signWith(SignatureAlgorithm.HS256, secretKey)
    .compact();

 

생성된 Refresh Token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

 

 

4. Refresh Token 저장

데이터베이스에 Refresh Token 저장:

CREATE TABLE refresh_tokens (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT NOT NULL,
    token VARCHAR(500),
    expiry_date TIMESTAMP
);

 

 

저장된 데이터:

id    user_id    token                              expiry_date
1     1          eyJhbGciOiJIUzI1NiIsInR...        2024-12-20 10:00:00

 

5. 토큰 반환

Access Token은 응답 본문에 포함.

Refresh Token은 HttpOnly 쿠키로 반환:

Set-Cookie: refreshToken=eyJhbGciOiJIUzI1NiIsInR...; HttpOnly; Secure

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR..."
}

 

 

 

3. 인증된 API 요청

 

3.1 프론트엔드 (Access Token 포함 요청)

 

프론트엔드는 Access Token을 Authorization 헤더에 포함하여 요청:

GET /api/user/profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR...

 

 

 

 

3.2 백엔드 (Access Token 검증)

 

1. 서명(Signature) 검증

Access Token의 서명과 서버의 비밀 키(Secret Key)를 사용해 서명이 변조되지 않았는지 확인:

Claims claims = Jwts.parser()
    .setSigningKey(secretKey)
    .parseClaimsJws(accessToken)
    .getBody();

 

 

2. 만료 시간(exp) 확인

Access Token의 exp 값이 현재 시간보다 이후인지 확인:

Date expiration = claims.getExpiration();
if (expiration.before(new Date())) {
    throw new ExpiredJwtException(...);
}

 

 

3. 요청 처리

Access Token의 sub(사용자 ID)을 사용해 사용자 정보를 조회하고 응답:

{
  "id": 1,
  "username": "user1",
  "email": "user1@example.com"
}

 

 

 

4. Access Token 만료

 

4.1 프론트엔드 (재발급 요청)

 

프론트엔드는 401 응답(Access Token 만료)을 받으면 Refresh Token으로 새로운 Access Token 요청:

POST /api/token/refresh
Content-Type: application/json

 

 

 

4.2 백엔드 (Refresh Token 검증 및 재발급)

 

1. Refresh Token 검증

HttpOnly 쿠키로 전달된 Refresh Token을 검증:

서명(Signature) 확인.

만료 시간(exp) 확인.

데이터베이스 확인:

SELECT * FROM refresh_tokens WHERE token = 'eyJhbGciOiJIUzI1NiIsInR...';

 

 

2. 새로운 Access Token 생성

Refresh Token이 유효하면 새로운 Access Token을 생성.

3. 새로운 Access Token 반환

클라이언트에 새로운 Access Token 반환:

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR..."
}

 

5. 최종 저장 및 흐름 요약

 

데이터 저장 위치

 

1. Access Token:

클라이언트 측에 저장 (로컬 스토리지, 세션 스토리지).

2. Refresh Token:

백엔드에서 데이터베이스에 저장 및 클라이언트의 HttpOnly 쿠키.

 

전체 흐름 요약

 

1. 회원가입:

사용자 정보(암호화된 비밀번호 포함)가 데이터베이스에 저장됨.

2. 로그인:

백엔드에서 Access Token과 Refresh Token을 생성. Refresh Token은 서버와 클라이언트 모두 저장.

3. API 요청:

Access Token으로 요청. 만료 시 Refresh Token으로 재발급.

4. Access Token 만료:

백엔드는 Refresh Token을 검증해 새로운 Access Token을 생성.

'spring' 카테고리의 다른 글

[Spring] Spring에서의 동기 비동기  (0) 2025.01.06
[Spring] Jackson  (0) 2024.12.17
[Spring] JPA 연관관계  (0) 2024.12.04
[Spring] JPA  (0) 2024.12.03
[Spring] 프록시(proxy)  (0) 2024.11.30