백엔드

JWT 인증 패턴

백엔드

JWT 인증 패턴 — 실전 적용 구조와 코드 예시

언제 쓰나 · SPA + API 구조에서 토큰 기반 인증이 필요할 때 · 로그아웃 시 토큰 무효화가 필요할 때

#backend#JWT
다운로드
# JWT 인증 패턴

## 언제 쓰는가
- SPA + API 구조에서 토큰 기반 인증이 필요할 때
- 로그아웃 시 토큰 무효화가 필요할 때

## 핵심 구조
- HS256 서명 (Secret Key 환경변수 주입)
- userId / email / role 클레임
- 블랙리스트 기반 로그아웃
- 스케줄러로 만료 토큰 정리

## 코드 예시

### 토큰 생성

```java
public String generateToken(UUID userId, String email, String role) {
    return Jwts.builder()
        .setSubject(userId.toString())
        .claim("email", email)
        .claim("role", role)
        .setIssuedAt(new Date())
        .setExpiration(new Date(System.currentTimeMillis() + expiration))
        .signWith(Keys.hmacShaKeyFor(secret.getBytes(UTF_8)))
        .compact();
}
```

### 토큰 검증 + 블랙리스트

```java
private final ConcurrentHashMap<String, Long> blacklist = new ConcurrentHashMap<>();

public boolean validateToken(String token) {
    if (blacklist.containsKey(token)) return false;
    Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token);
    return true;
}

public void blacklistToken(String token) {
    long exp = getExpirationFromToken(token).getTime();
    blacklist.put(token, exp);
}

@Scheduled(fixedRate = 3600000)
public void cleanup() {
    long now = System.currentTimeMillis();
    blacklist.entrySet().removeIf(e -> e.getValue() < now);
}
```

### 필터 (요청에서 토큰 추출)

```java
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
    token = token.substring(7);
    if (jwtProvider.validateToken(token)) {
        String userId = jwtProvider.getUserIdFromToken(token);
        var auth = new UsernamePasswordAuthenticationToken(userId, null, authorities);
        SecurityContextHolder.getContext().setAuthentication(auth);
    }
}
```

## 주의사항
- JWT Secret은 반드시 환경변수로 주입 (하드코딩 금지)
- 블랙리스트는 인메모리 → 서버 재시작 시 초기화 (멀티 인스턴스면 Redis 전환)
- localStorage 저장은 XSS 취약 — HttpOnly 쿠키가 더 안전
- 만료 시간은 용도에 맞게 (웹 24h, 모바일 7~30d)

## 재사용 방법
- JwtTokenProvider + JwtFilter 복사
- `application.yml`에 `jwt.secret`, `jwt.expiration` 추가
- SecurityConfig에 `addFilterBefore(jwtFilter, ...)` 등록
jt · v1 · CC0-1.0 · 복사 0