반응형
build.gradle
- jjwt-api: JWT 기능을 사용하는 데 필요한 API.
- jjwt-impl: JWT 서명 및 파싱 등의 구현체.
- jjwt-jackson: JSON 파싱을 위한 Jackson 연동.
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")
JWT
- 밑은 JWT 관리 클래스이다. 한 번 다시 공부해보자
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;
@Component
public class JwtUtil {
private Key key;
private final long accessTokenValidity = 1000 * 60 * 1; // 15분
private final long refreshTokenValidity = 1000L * 60 * 60 * 24 * 7; // 7일
public JwtUtil(@Value("${jwt.secret}") String secretKeyRaw) {
this.key = Keys.hmacShaKeyFor(secretKeyRaw.getBytes());
}
public String generateAccessToken(Long userId) {
return generateToken(userId, accessTokenValidity);
}
public String generateRefreshToken(Long userId) {
return generateToken(userId, refreshTokenValidity);
}
private String generateToken(Long userId, long validityInMs) {
Date now = new Date();
Date expiry = new Date(now.getTime() + validityInMs);
return Jwts.builder()
.setSubject(String.valueOf(userId))
.setIssuedAt(now)
.setExpiration(expiry)
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}
public void validateToken(String token) {
try {
log.info("token {}", token);
Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token);
} catch (JwtException | IllegalArgumentException e) {
throw UnauthorizedException.of("유효하지 않은 refresh token입니다.");
}
}
public Long getUserIdFromToken(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
return Long.parseLong(claims.getSubject());
}
public Date getExpiration(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
return claims.getExpiration();
}
}
SecretKey 및 유효기간
- key: JWT 서명 및 검증에 사용될 비밀 키 (HS256 대칭 키)
- @Value("${jwt.secret}"): application.properties 또는 application.yml에 정의된 jwt.secret 값을 주입받음
- Keys.hmacShaKeyFor(...): HAMC 알고리즘(예 : HS256)에 맞는 javax,crypto.SecretKey를 생성 → 즉, HS256에서 요구하는 대칭키(서명용 비밀 키)를 만들기 위해 secretKeyRaw를 바이트 배열로 변환해서 키로 쓰는 것
- accessTokenValidity: 액세스 토큰 만료 시간 → 1분(임시로 1분)
- refreshTokenValidity: 리프레시 토큰 만료 시간 → 7일
private Key key;
private final long accessTokenValidity = 1000 * 60 * 1; // 1분
private final long refreshTokenValidity = 1000L * 60 * 60 * 24 * 7; // 7일
public JwtUtil(@Value("${jwt.secret}") String secretKeyRaw) {
this.key = Keys.hmacShaKeyFor(secretKeyRaw.getBytes());
}
토큰 생성
public String generateAccessToken(Long userId) {
return generateToken(userId, accessTokenValidity);
}
public String generateRefreshToken(Long userId) {
return generateToken(userId, refreshTokenValidity);
}
private String generateToken(Long userId, long validityInMs) {
Date now = new Date();
Date expiry = new Date(now.getTime() + validityInMs);
return Jwts.builder()
.setSubject(String.valueOf(userId)) // 토큰에 userId를 subject로 설정
.setIssuedAt(now) // 발급 시각
.setExpiration(expiry) // 만료 시각
.signWith(key, SignatureAlgorithm.HS256) // HS256으로 서명
.compact();
}
- 각각 Access/Refresh Token을 생성하며 내부적으로 generateToken(...)을 호출
결과는 다음과 같다.
- sub(Subject): 유저 ID
- iat(Issued At): 발급 시각
- exp(Expiration): 만료 시각
- 서명: HMAC-SHA256 사용
토큰 유효성 검사
public void validateToken(String token) {
try {
log.info("token {}", token);
Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token);
} catch (JwtException | IllegalArgumentException e) {
throw UnauthorizedException.of("유효하지 않은 refresh token입니다.");
}
}
- 성공 시: 아무 예외 없이 통과.
- 실패 시: JwtException 또는 IllegalArgumentException 발생 → UnauthorizedException 발생시킴.
토큰에서 유저 ID 추출
public Long getUserIdFromToken(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
return Long.parseLong(claims.getSubject());
}
- subject에 저장된 userId를 가져와 Long으로 변환하여 반환함
만료일 추출
public Date getExpiration(String token) {
Claims claims = Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody();
return claims.getExpiration();
}
- JWT의 exp 필드를 반환함 → 토큰 만료 시각 확인 용도로 사용됨
예시 토큰 구조 → 생성결과
{
"sub": "4", // userId
"iat": 1693000000,
"exp": 1693000900
}
반응형
'프로젝트 > kkrap' 카테고리의 다른 글
| [Spring boot] JWT Accesstoken 및 Refresh 검사 흐름? (3) | 2025.08.08 |
|---|---|
| [Spring boot] JWT 로그인 요청 검증 방법 (3) | 2025.08.08 |
| JWT 기반 인증 도입 개선 문서 (2) | 2025.08.03 |
| JWT란? (5) | 2025.08.03 |
| 메인페이지 무한 스크롤 기능 (1) | 2025.07.30 |