Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public TokenReissueResponse reissueAccessToken(String authorizationHeader) {
Long userId = jwtProvider.getUserIdFrom(authorizationHeader);

User user = userRepository.findById(userId)
.orElseThrow(() -> new JwtException(JwtErrorCode.INVALID_JWT_TOKEN));
.orElseThrow(() -> new JwtException(JwtErrorCode.INVALID_TOKEN));

String providedToken = jwtProvider.resolveToken(authorizationHeader);
user.validateRefreshToken(providedToken);
Expand Down
44 changes: 30 additions & 14 deletions src/main/java/org/terning/terningserver/auth/jwt/JwtProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,16 @@
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SecurityException;
import jakarta.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import javax.crypto.SecretKey;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.terning.terningserver.auth.dto.Token;
import org.terning.terningserver.auth.jwt.exception.JwtErrorCode;
import org.terning.terningserver.auth.jwt.exception.JwtException;
import org.terning.terningserver.common.config.ValueConfig;


import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;

@Component
@RequiredArgsConstructor
public class JwtProvider {
Expand All @@ -28,11 +26,12 @@ public class JwtProvider {
private static final String TOKEN_PREFIX = "Bearer ";

private final ValueConfig valueConfig;

private SecretKey secretKey;

@PostConstruct
protected void init() {
secretKey = Keys.hmacShaKeyFor(valueConfig.getSecretKey().getBytes(StandardCharsets.UTF_8));
this.secretKey = Keys.hmacShaKeyFor(valueConfig.getSecretKey().getBytes(StandardCharsets.UTF_8));
}

public Token generateTokens(Long userId) {
Expand All @@ -48,10 +47,9 @@ public Token generateAccessToken(Long userId) {

public Long getUserIdFrom(String authorizationHeader) {
String token = resolveToken(authorizationHeader);

Claims claims = parseClaims(token);

Object userIdClaim = claims.get(USER_ID_CLAIM);

if (userIdClaim instanceof Number) {
return ((Number) userIdClaim).longValue();
}
Expand All @@ -73,21 +71,39 @@ private String generateToken(Long userId, long expiration) {
.setClaims(claims)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(secretKey)
.signWith(this.secretKey)
.compact();
}

private Claims parseClaims(String token) {
try {
return Jwts.parserBuilder()
.setSigningKey(secretKey)
.setSigningKey(this.secretKey)
.build()
.parseClaimsJws(token)
.getBody();
} catch (ExpiredJwtException e) {
throw new JwtException(JwtErrorCode.EXPIRED_JWT_TOKEN);
} catch (UnsupportedJwtException | MalformedJwtException | SecurityException | IllegalArgumentException e) {
throw new JwtException(JwtErrorCode.INVALID_JWT_TOKEN);
} catch (Exception e) {
handleJwtException(e);
throw new JwtException(JwtErrorCode.UNEXPECTED_ERROR);
}
}

private void handleJwtException(Exception e) {
if (e instanceof ExpiredJwtException) {
throw new JwtException(JwtErrorCode.EXPIRED_TOKEN);
}
if (e instanceof SecurityException) {
throw new JwtException(JwtErrorCode.SIGNATURE_ERROR);
}
if (e instanceof MalformedJwtException) {
throw new JwtException(JwtErrorCode.MALFORMED_TOKEN);
}
if (e instanceof UnsupportedJwtException) {
throw new JwtException(JwtErrorCode.UNSUPPORTED_TOKEN);
}
if (e instanceof IllegalArgumentException) {
throw new JwtException(JwtErrorCode.EMPTY_TOKEN);
}
throw new JwtException(JwtErrorCode.INVALID_TOKEN);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,19 @@
@Getter
@AllArgsConstructor
public enum JwtErrorCode {
INVALID_JWT_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 JWT 토큰입니다."),
INVALID_USER_ID(HttpStatus.BAD_REQUEST, "유효하지 않은 userId 값입니다."),
INVALID_USER_ID_TYPE(HttpStatus.BAD_REQUEST, "유효하지 않은 userId 타입입니다."),
INVALID_USER_DETAILS_TYPE(HttpStatus.INTERNAL_SERVER_ERROR, "유효하지 않은 UserDetail 타입입니다."),
TOKEN_NOT_FOUND(HttpStatus.UNAUTHORIZED, "Authorization 헤더에 토큰이 없습니다."),
EXPIRED_JWT_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 JWT 토큰입니다."),
;

public static final String PREFIX = "[JWT ERROR]";
INVALID_USER_ID_TYPE(HttpStatus.BAD_REQUEST, "사용자 ID의 타입이 유효하지 않습니다."),
EMPTY_TOKEN(HttpStatus.BAD_REQUEST, "토큰이 비어있거나 유효하지 않은 형식입니다."),

private final HttpStatus status;
private final String rawMessage;
TOKEN_NOT_FOUND(HttpStatus.UNAUTHORIZED, "HTTP Authorization 헤더를 찾을 수 없습니다."),
EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, "만료된 토큰입니다."),
MALFORMED_TOKEN(HttpStatus.UNAUTHORIZED, "잘못된 형식의 토큰입니다."),
SIGNATURE_ERROR(HttpStatus.UNAUTHORIZED, "토큰 서명 검증에 실패했습니다."),
UNSUPPORTED_TOKEN(HttpStatus.UNAUTHORIZED, "지원되지 않는 방식의 토큰입니다."),
INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다."),

UNEXPECTED_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "토큰 처리 중 예상치 못한 서버 오류가 발생했습니다.");

public String getMessage() {
return PREFIX + " " + rawMessage;
}
private final HttpStatus status;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package org.terning.terningserver.common.config;

import jakarta.annotation.PostConstruct;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

@Configuration
@Getter
public class ValueConfig {
Expand All @@ -22,4 +26,9 @@ public class ValueConfig {

@Value("${jwt.refresh-token-expired}")
private Long refreshTokenExpired;

@PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes(StandardCharsets.UTF_8));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public void updateProfile(String name, ProfileImage profileImage){

public void validateRefreshToken(String providedToken) {
if (this.refreshToken == null || !this.refreshToken.equals(providedToken)) {
throw new JwtException(JwtErrorCode.INVALID_JWT_TOKEN);
throw new JwtException(JwtErrorCode.INVALID_TOKEN);
}
}
}
Loading