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
4 changes: 4 additions & 0 deletions src/main/java/org/terning/terningserver/auth/dto/Token.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.terning.terningserver.auth.dto;

public record Token(String accessToken, String refreshToken) {
}
93 changes: 93 additions & 0 deletions src/main/java/org/terning/terningserver/auth/jwt/JwtProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package org.terning.terningserver.auth.jwt;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SecurityException;
import jakarta.annotation.PostConstruct;
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 {

private static final String USER_ID_CLAIM = "userId";
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));
}

public Token generateTokens(Long userId) {
String accessToken = generateToken(userId, valueConfig.getAccessTokenExpired());
String refreshToken = generateToken(userId, valueConfig.getRefreshTokenExpired());
return new Token(accessToken, refreshToken);
}

public Token generateAccessToken(Long userId) {
String accessToken = generateToken(userId, valueConfig.getAccessTokenExpired());
return new Token(accessToken, null);
}

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();
}
throw new JwtException(JwtErrorCode.INVALID_USER_ID_TYPE);
}

public String resolveToken(String rawToken) {
if (rawToken != null && rawToken.startsWith(TOKEN_PREFIX)) {
return rawToken.substring(TOKEN_PREFIX.length());
}
throw new JwtException(JwtErrorCode.TOKEN_NOT_FOUND);
}

private String generateToken(Long userId, long expiration) {
Claims claims = Jwts.claims();
claims.put(USER_ID_CLAIM, userId);

return Jwts.builder()
.setClaims(claims)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(secretKey)
.compact();
}

private Claims parseClaims(String token) {
try {
return Jwts.parserBuilder()
.setSigningKey(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);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.terning.terningserver.common.security.jwt.exception;
package org.terning.terningserver.auth.jwt.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;
Expand All @@ -11,6 +11,8 @@ public enum JwtErrorCode {
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]";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.terning.terningserver.common.security.jwt.exception;
package org.terning.terningserver.auth.jwt.exception;

import lombok.Getter;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
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 @@ -26,9 +22,4 @@ 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 @@ -13,8 +13,8 @@
import org.terning.terningserver.auth.common.exception.AuthException;
import org.terning.terningserver.common.exception.dto.ErrorResponse;
import org.terning.terningserver.common.exception.enums.ErrorMessage;
import org.terning.terningserver.common.security.jwt.exception.JwtErrorCode;
import org.terning.terningserver.common.security.jwt.exception.JwtException;
import org.terning.terningserver.auth.jwt.exception.JwtErrorCode;
import org.terning.terningserver.auth.jwt.exception.JwtException;

@RestControllerAdvice
@Slf4j
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.terning.terningserver.common.security.jwt.exception.JwtErrorCode;
import org.terning.terningserver.auth.jwt.exception.JwtErrorCode;

import java.util.Map;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.terning.terningserver.common.security.jwt.exception.JwtErrorCode;
import org.terning.terningserver.common.security.jwt.exception.JwtException;
import org.terning.terningserver.auth.jwt.exception.JwtErrorCode;
import org.terning.terningserver.auth.jwt.exception.JwtException;
import org.terning.terningserver.common.security.jwt.auth.JwtClaimsParser;

@Component
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.terning.terningserver.common.config.ValueConfig;
import org.terning.terningserver.common.security.jwt.exception.JwtErrorCode;
import org.terning.terningserver.common.security.jwt.exception.JwtException;
import org.terning.terningserver.auth.jwt.exception.JwtErrorCode;
import org.terning.terningserver.auth.jwt.exception.JwtException;
import org.terning.terningserver.common.security.jwt.provider.JwtKeyProvider;

@Service
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.terning.terningserver.common.security.jwt.auth;

import org.terning.terningserver.common.security.jwt.exception.JwtErrorCode;
import org.terning.terningserver.common.security.jwt.exception.JwtException;
import org.terning.terningserver.auth.jwt.exception.JwtErrorCode;
import org.terning.terningserver.auth.jwt.exception.JwtException;

public class UserIdConverter {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import org.terning.terningserver.common.security.jwt.exception.JwtErrorCode;
import org.terning.terningserver.common.security.jwt.exception.JwtException;
import org.terning.terningserver.auth.jwt.exception.JwtErrorCode;
import org.terning.terningserver.auth.jwt.exception.JwtException;

import java.io.IOException;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.terning.terningserver.external.pushNotification.user.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
Expand All @@ -24,7 +25,7 @@ public class UserSyncEvent {

private Long userId;

@Enumerated
@Enumerated(EnumType.STRING)
private UserSyncEventType eventType;

private String newValue;
Expand Down
Loading