refreshToken 을 안전하게 생성

By | 2026년 2월 20일
Table of Contents

refreshToken 을 안전하게 생성

refreshToken 은 서버에서 Set-Cookie: HttpOnly; Secure; SameSite=Strict 로 클라이언트 전송하는 방법을 설명합니다.

Cookie 생성 유틸리티 클래스

import jakarta.servlet.http.Cookie;
import org.springframework.http.ResponseCookie;
import org.springframework.stereotype.Component;

@Component
public class CookieUtil {

    public ResponseCookie createRefreshTokenCookie(String refreshToken, long maxAgeInSeconds) {
        return ResponseCookie.from("refreshToken", refreshToken)
                .httpOnly(true)    // JS 접근 방지
                .secure(true)      // HTTPS 환경에서만 전송
                .path("/")         // 모든 경로에서 쿠키 전송
                .maxAge(maxAgeInSeconds)
                .sameSite("Strict") // CSRF 공격 방어
                .build();
    }
}

컨트롤러에서 쿠키 전송하기

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    private final CookieUtil cookieUtil;

    public AuthController(CookieUtil cookieUtil) {
        this.cookieUtil = cookieUtil;
    }

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
        // 1. 사용자 인증 로직 (생략)
        // 2. 토큰 생성
        String accessToken = "access-token-string";
        String refreshToken = "refresh-token-string";

        // 3. Refresh Token을 담은 쿠키 생성
        ResponseCookie cookie = cookieUtil.createRefreshTokenCookie(refreshToken, 7 * 24 * 60 * 60);

        // 4. Access Token은 바디에, Refresh Token은 헤더(쿠키)에 설정
        return ResponseEntity.ok()
                .header(HttpHeaders.SET_COOKIE, cookie.toString())
                .body(new LoginResponse(accessToken)); 
    }
}

클라이언트로부터 쿠키 읽기

@PostMapping("/refresh")
public ResponseEntity<?> refresh(
    @CookieValue(name = "refreshToken") String refreshToken) {

    // 1. 유효성 검증 및 새로운 Access Token 생성 로직
    if (isValid(refreshToken)) {
        String newAccessToken = "new-access-token";
        return ResponseEntity.ok(new LoginResponse(newAccessToken));
    }

    return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}

application-dev.yml, application-prd.yml 에서 Secure 처리

Secure(true) 설정 시, http://localhost 환경에서는 쿠키가 저장되지 않을 수 있습니다.

application-dev.yml

auth:
  cookie:
    secure: false
    same-site: Laxs # 로컬 개발 시에는 Strict보다 Lax가 편리할 수 있습니다.

application-prd.yml

auth:
  cookie:
    secure: true
    same-site: Strict
@Component
public class CookieUtil {

    @Value("${auth.cookie.secure}")
    private boolean isSecure;

    @Value("${auth.cookie.same-site}")
    private String sameSite;

    public ResponseCookie createRefreshTokenCookie(String refreshToken, long maxAgeInSeconds) {
        return ResponseCookie.from("refreshToken", refreshToken)
                .httpOnly(true)
                .secure(isSecure)     // yml 설정값 적용
                .path("/")
                .maxAge(maxAgeInSeconds)
                .sameSite(sameSite)    // yml 설정값 적용
                .build();
    }
}

주의사항

  • CORS 설정: 프론트엔드와 백엔드 도메인이 다를 경우, 백엔드 CORS 설정에서 allowCredentials(true)를 반드시 활성화해야 쿠키가 주고받아집니다.

  • Local 테스트: Secure(true) 설정 시, http://localhost 환경에서는 쿠키가 저장되지 않을 수 있습니다. 개발 환경에서는 Secure(false)로 운영하거나 HTTPS 대행 프록시를 사용하세요.

답글 남기기