Spring Boot + JWT + Refresh Token 로그인 & 인증/인가 구현

dev.hyjang·2025년 8월 8일

이번 글에서는 Spring Boot + JWT 기반의 로그인 기능에 Refresh Token 기능을 추가하고,
토큰 재발급 및 로그아웃 처리까지 구현한 과정을 기록합니다.


구현 목표

  • Access Token + Refresh Token 기반 인증 구조 설계
  • Access Token 만료 시 Refresh Token으로 재발급
  • Refresh Token 저장·관리
  • 로그아웃 시 Refresh Token 삭제 처리
  • Postman을 활용한 전체 테스트

1. Refresh Token 설계

저장 위치

  • DB에 저장하여 관리 (user_info 테이블에 컬럼 추가)
ALTER TABLE user_info ADD refresh_token VARCHAR(255);

저장 이유

  • Refresh Token은 장기 보관이 필요한 토큰이므로 서버에서 안전하게 관리
  • 클라이언트와 서버 양쪽에서 토큰을 검증 가능

2. JWT 발급 구조

Access Token

  • 만료 시간: 짧게 (예: 1시간)
  • 인증/인가 요청 시 사용
  • 탈취 시 피해를 최소화하기 위해 짧은 수명 설정

Refresh Token

  • 만료 시간: 길게 (예: 7일)
  • Access Token이 만료됐을 때 새로운 Access Token 발급에만 사용
  • DB에 저장하여 유효성 검증 가능

3. API 설계

API 명MethodURL설명
로그인POST/api/loginID/PW 검증 후 토큰 발급
인증 테스트GET/api/user/testAccess Token 유효성 검증
토큰 재발급POST/api/refreshRefresh Token으로 Access Token 재발급
로그아웃POST/api/logoutRefresh Token 삭제

4. 구현 핵심 코드

로그인 시 Refresh Token 저장

String refreshToken = jwtUtil.createRefreshToken(username);
userMapper.updateRefreshToken(username, refreshToken);

return Map.of(
    "accessToken", accessToken,
    "refreshToken", refreshToken
);

토큰 재발급

@PostMapping("/api/refresh")
public Map<String, String> refreshToken(@RequestBody Map<String, String> request) {
    String refreshToken = request.get("refreshToken");

    if (!jwtUtil.validateToken(refreshToken)) {
        throw new RuntimeException("Refresh Token이 유효하지 않습니다.");
    }

    String username = jwtUtil.getUsername(refreshToken);
    String savedRefreshToken = userMapper.findRefreshToken(username);

    if (!refreshToken.equals(savedRefreshToken)) {
        throw new RuntimeException("Refresh Token 불일치");
    }

    String newAccessToken = jwtUtil.createAccessToken(username);
    return Map.of("accessToken", newAccessToken);
}

로그아웃 처리

@PostMapping("/api/logout")
public void logout(@RequestBody Map<String, String> request) {
    String refreshToken = request.get("refreshToken");
    String username = jwtUtil.getUsername(refreshToken);
    userMapper.updateRefreshToken(username, null);
}

5. Postman 테스트 시퀀스

1) 로그인 요청

  • POST /api/login
  • Body(JSON)
json
{ "username": "testuser", "password": "1234" }
응답: Access Token + Refresh Token

2) 인증 API 호출

  • GET /api/user/test
  • Header
css
Authorization: Bearer {accessToken}

3) Access Token 만료 시 재발급

  • POST /api/refresh
  • Body(JSON)
json
{ "refreshToken": "발급받은_refresh_token" }
응답: 새 Access Token

4) 로그아웃

  • POST /api/logout
  • Body(JSON)
json
{ "refreshToken": "발급받은_refresh_token" }

5) 로그아웃 후 재발급 요청 테스트

  • POST /api/refresh
  • Body(JSON)
json
{ "refreshToken": "발급받은_refresh_token" }
  • 응답:
mathematica
Refresh Token 불일치
  • 로그아웃 후에는 DB에서 Refresh Token이 삭제되어 재발급 불가

6. 마무리

  • JWT 기반의 인증/인가 구조 완성
  • Refresh Token을 통한 안전한 Access Token 재발급
  • 로그아웃 시 Refresh Token 무효화
  • Postman으로 로그인 → 인증 → 재발급 → 로그아웃 → 재발급 실패 시나리오 검증 완료

다음 단계에서는:

  • Refresh Token을 Redis 같은 인메모리 저장소로 관리
  • HTTPS 적용
  • 토큰 탈취 방지 로직 추가
profile
낭만감자

0개의 댓글