AuthEntity.java
역할
- DB에서 refreshToken 정보를 관리하는 JPA 엔티티
- 토큰 조회, 만료, 폐기 시 데이터 저장/조회 담당
- Single Session 및 보안 감사 기능 지원
1. 구조
@Getter
@Entity
@Table(name = "refresh_tokens", indexes = {
@Index(name = "idx_token_hash", columnList = "tokenHash"),
// tokenHash 단건 조회 성능용 인덱스
// refresh 시 hash로 바로 찾기 때문에 거의 PK급 중요도
@Index(name = "idx_user_revoked", columnList = "user_id, revoked_at")
// 유저의 "활성 토큰" 조회용 복합 인덱스
// where user_id = ? and revoked_at is null
// 로그아웃 시 전체 토큰 무효화할 때도 성능에 도움
})
@AllArgsConstructor(access = AccessLevel.PROTECTED)
// 파라미터 없는 기본 생성자를 protected 접근제어자로 만들겠다.
// JPA 기본 생성자 강제 조건 만족 + 외부에서 new 못하게 막음
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
public class AuthEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// PK는 단순 식별용
// refresh 토큰은 tokenHash 기준으로 조회
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false) // FK
private UserEntity user;
// fetch = FetchType.LAZY -> AuthEntity만 조회할 때 UserEntity를 안가져오기 위함
// UserEntity를 가져오고 싶을 땐 auth.getUser()를 하면 SELECT문 실행해서 가져옴
// 불필요한 조인 방지 = 성능 최적화
@Column(nullable = false, unique = true, length = 64)
private String tokenHash;
// refreshToken을 그대로 저장하지 않고 HMAC_SHA_256 해시값만 저장
// DB 털려도 원본 토큰 복원 불가
// length=64는 SHA-256 hex 길이 (32byte → 64문자)
@Column(name = "expires_at", nullable = false)
private LocalDateTime expiresAt;
// refresh 토큰 만료 시간
// 만료 검사 시 DB + JWT exp 둘 다 검증하면 더 안전
@Column(name = "revoked_at")
private LocalDateTime revokedAt;
// 로그아웃/강제 폐기 시각
// null이면 활성 토큰
// 값 있으면 폐기된 토큰 (재사용 공격 방지용)
@Column(name = "created_at", updatable = false, nullable = false)
private LocalDateTime createdAt;
// 생성 시각
// 최초 발급 시점 기록
// 보안 감사 / 토큰 이상 탐지 시 활용 가능
@PrePersist
void prePersist() {
this.createdAt = LocalDateTime.now();
if (this.expiresAt == null) {
this.expiresAt = LocalDateTime.now().plusDays(7);
}
// expiresAt 기본값 7일
// JWT refresh 만료 기간과 반드시 동일하게 맞춰야 함
}
public void setTokenHash(String tokenHash) {
this.tokenHash = tokenHash;
// refresh rotation 시 hash 교체용
}
public void setExpiresAt(LocalDateTime expiresAt) {
this.expiresAt = expiresAt;
// rotation 시 만료 갱신
}
public void setRevokedAt(LocalDateTime revokedAt) {
this.revokedAt = revokedAt;
// 로그아웃 / 재사용 감지 시 즉시 무효화
}
}
2. 특징 및 장점
- tokenHash
- refreshToken을 HMAC_SHA_256 해시값으로 저장
- DB가 털려도 원본 토큰 복원 불가 → 안전성 확보
- revokedAt
- 로그아웃/강제 폐기 시각 기록
- null → 활성 토큰, 값 존재 → 폐기 토큰
- expiresAt
- refreshToken 만료 시각
- JWT exp와 함께 검증하면 더 안전
- createdAt
- 토큰 발급 시각 기록 → 보안 감사/이상 탐지 활용
- Indexes
idx_token_hash → 단건 조회 성능 최적화
idx_user_revoked → 사용자 활성 토큰 조회, 로그아웃 전체 폐기 시 성능 최적화
3. 설명 포인트
- refreshToken을 그대로 DB에 저장하지 않고 해시값(tokenHash)만 저장
- Single Session 정책 구현 가능: 이전 활성 토큰 폐기
- JPA
fetch = FetchType.LAZY → 불필요한 UserEntity 조회 방지, 성능 최적화