Skip to content

Commit

Permalink
[Refacor] 리프레시 토큰 중복 발급 문제 해결 및 로그인 코드 리팩토링
Browse files Browse the repository at this point in the history
[Refacor] 리프레시 토큰 중복 발급 문제 해결 및 로그인 코드 리팩토링
  • Loading branch information
Jeongmin39 authored Jan 3, 2025
2 parents b7b5efb + 2fc0bcc commit e51e62c
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,16 @@
import com.spot.spotserver.api.auth.dto.request.TokenRequest;
import com.spot.spotserver.api.auth.dto.response.TokenResponse;
import com.spot.spotserver.api.auth.service.AuthService;
import com.spot.spotserver.api.user.service.UserService;
import com.spot.spotserver.common.payload.ApiResponse;
import com.spot.spotserver.common.payload.SuccessCode;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
public class AuthController {

private final AuthService authService;
private final UserService userService;

@PostMapping("/api/login/kakao")
public ApiResponse<TokenResponse> login(@RequestParam final String accessToken) {
Expand All @@ -34,7 +29,7 @@ public ApiResponse<TokenResponse> login(@RequestParam final String accessToken)
@PostMapping("/api/refresh")
public ApiResponse<TokenResponse> reissueToken(@RequestBody TokenRequest request) {

TokenResponse result = userService.reissueToken(request.refreshToken());
TokenResponse result = authService.reissueToken(request.refreshToken());
return ApiResponse.success(SuccessCode.REISSUE_TOKEN_SUCCESS, result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
public class JwtTokenProvider {

private static final String USER_ID = "userId";
private static final Long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 60 * 1000L * 24; // 1일
private static final Long ACCESS_TOKEN_EXPIRATION_TIME = 60 * 60 * 1000L * 5; // 5시간
private static final Long REFRESH_TOKEN_EXPIRATION_TIME = 60 * 60 * 1000L * 24 * 14; // 14일

@Value("${jwt.secret}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,19 @@ public class RefreshTokenService {

@Transactional
public void saveRefreshToken(final Long userId, final String refreshToken) {
// 기존 리프레시 토큰 삭제
deleteRefreshToken(userId);
// 새로운 리프레시 토큰 저장
String strUserId = userId.toString();
if (tokenRepository.existsById(strUserId)) {
tokenRepository.deleteById(strUserId);
}
tokenRepository.save(Token.of(userId, refreshToken));
}

public String getRefreshToken(final Long userId) {
return tokenRepository.findById(userId.toString())
.map(Token::getRefreshToken)
.orElse(null);
}

public void deleteRefreshToken(final Long userId) {
String strUserId = userId.toString();
if (tokenRepository.existsById(strUserId)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.spot.spotserver.api.auth.jwt.redis;

import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;

@RedisHash(value = "Token", timeToLive = 60 * 60 * 24 * 14)
@RedisHash(value = "refreshToken", timeToLive = 60 * 60 * 24 * 14)
@AllArgsConstructor
@Getter
@Builder
Expand All @@ -18,11 +18,11 @@ public class Token {
private String refreshToken;

public static Token of(
final Long id,
final Long userId,
final String refreshToken
) {
return Token.builder()
.id(id.toString())
.id(userId.toString())
.refreshToken(refreshToken)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,5 @@

import org.springframework.data.repository.CrudRepository;

import java.util.Optional;

public interface TokenRepository extends CrudRepository<Token, String> {
}
63 changes: 32 additions & 31 deletions src/main/java/com/spot/spotserver/api/auth/service/AuthService.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package com.spot.spotserver.api.auth.service;

import com.spot.spotserver.api.auth.client.KakaoApiClient;
import com.spot.spotserver.api.auth.client.KakaoAuthApiClient;
import com.spot.spotserver.api.auth.dto.response.KakaoAccessTokenResponse;
import com.spot.spotserver.api.auth.dto.response.KakaoUserResponse;
import com.spot.spotserver.api.auth.dto.response.TokenResponse;
import com.spot.spotserver.api.auth.exception.JwtCustomException;
import com.spot.spotserver.api.auth.exception.OAuth2TokenException;
import com.spot.spotserver.api.auth.handler.UserAuthentication;
import com.spot.spotserver.api.auth.jwt.JwtTokenProvider;
import com.spot.spotserver.api.auth.jwt.JwtValidationType;
import com.spot.spotserver.api.auth.jwt.redis.RefreshTokenService;
import com.spot.spotserver.api.user.domain.User;
import com.spot.spotserver.api.user.exception.UserNotFoundException;
import com.spot.spotserver.api.user.repository.UserRepository;
import com.spot.spotserver.api.user.service.UserService;
import com.spot.spotserver.common.payload.ErrorCode;
import feign.FeignException;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -24,17 +23,10 @@
@RequiredArgsConstructor
public class AuthService {

private static final String AUTH_CODE = "authorization_code";
private static final String REDIRECT_URI = "http://localhost:8080/api/login/kakao";

@Value("${spring.security.oauth2.client.registration.kakao.client-id}")
private String clientId;

@Value("${spring.security.oauth2.client.registration.kakao.client-secret}")
private String clientSecret;

private final KakaoApiClient kakaoApiClient;
private final KakaoAuthApiClient kakaoAuthApiClient;
private final JwtTokenProvider jwtTokenProvider;
private final UserService userService;
private final UserRepository userRepository;
Expand All @@ -47,39 +39,48 @@ public TokenResponse login(final String accessToken) {
}

// 카카오 액세스 토큰으로 사용자 정보 가져오기
KakaoUserResponse userResponse;
try {
userResponse = getUserInfo(accessToken);
} catch (FeignException e) {
throw new OAuth2TokenException(ErrorCode.USER_INFO_REQUEST_FAILED);
}
KakaoUserResponse userResponse = getUserInfo(accessToken);
Long userId = userService.processUser(userResponse);

// 기존 리프레시 토큰 확인 및 재사용
String existingRefreshToken = refreshTokenService.getRefreshToken(userId);
String jwtRefreshToken = existingRefreshToken != null
? existingRefreshToken
: jwtTokenProvider.issueRefreshToken(new UserAuthentication(userId, null, null));

// 서비스 자체 JWT 액세스 및 리프레시 토큰 생성
UserAuthentication authentication = new UserAuthentication(userResponse.id(), null, null);
String jwtAccessToken = jwtTokenProvider.issueAccessToken(authentication);
String jwtRefreshToken = jwtTokenProvider.issueRefreshToken(authentication);
// Redis에 리프레시 토큰 저장
refreshTokenService.saveRefreshToken(userService.getIdBySocialId(userResponse.id()), jwtRefreshToken);

// 리프레시 토큰 레디스에 저장
refreshTokenService.saveRefreshToken(userResponse.id(), jwtRefreshToken);
// 액세스 토큰 생성
String jwtAccessToken = jwtTokenProvider.issueAccessToken(new UserAuthentication(userId, null, null));

return processUser(userResponse);
return TokenResponse.of(jwtAccessToken, jwtRefreshToken);
}

private KakaoUserResponse getUserInfo(final String accessToken) {
return kakaoApiClient.getUserInformation("Bearer " + accessToken);
}

private TokenResponse processUser(KakaoUserResponse userResponse) {
if (userService.isExistingUser(userResponse.id())) {
return userService.getTokenByUserId(userService.getIdBySocialId(userResponse.id()));
} else {
return userService.getTokenByUserId(userService.createUser(userResponse));
}
}

public User getUserFromAccessToken(String accessToken) {
Long userId = jwtTokenProvider.getUserFromJwt(accessToken);
return userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException(ErrorCode.USER_NOT_FOUND));
}

public TokenResponse reissueToken(final String refreshToken) {
JwtValidationType validationType = jwtTokenProvider.validateToken(refreshToken);

if (validationType != JwtValidationType.VALID_JWT) {
throw new JwtCustomException(ErrorCode.INVALID_JWT_TOKEN);
}

Long userId = jwtTokenProvider.getUserFromJwt(refreshToken);
UserAuthentication userAuthentication = new UserAuthentication(userId, null, null);
String newAccessToken = jwtTokenProvider.issueAccessToken(userAuthentication);
String newRefreshToken = jwtTokenProvider.issueRefreshToken(userAuthentication);

// 새로운 리프레시 토큰으로 교체
refreshTokenService.saveRefreshToken(userId, newRefreshToken);
return TokenResponse.of(newAccessToken, newRefreshToken);
}
}
48 changes: 11 additions & 37 deletions src/main/java/com/spot/spotserver/api/user/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@

import com.spot.spotserver.api.auth.client.KakaoAccount;
import com.spot.spotserver.api.auth.dto.response.KakaoUserResponse;
import com.spot.spotserver.api.auth.dto.response.TokenResponse;
import com.spot.spotserver.api.auth.exception.JwtCustomException;
import com.spot.spotserver.api.auth.handler.UserAuthentication;
import com.spot.spotserver.api.auth.jwt.JwtTokenProvider;
import com.spot.spotserver.api.auth.jwt.JwtValidationType;
import com.spot.spotserver.api.auth.jwt.redis.RefreshTokenService;
import com.spot.spotserver.api.badge.domain.Badge;
import com.spot.spotserver.api.quiz.dto.UserBadgeResponse;
import com.spot.spotserver.api.badge.repository.BadgeRepository;
Expand Down Expand Up @@ -37,13 +31,19 @@
@RequiredArgsConstructor
public class UserService {

private final JwtTokenProvider jwtTokenProvider;
private final UserRepository userRepository;
private final LikesRepository likesRepository;
private final BadgeRepository badgeRepository;
private final RefreshTokenService refreshTokenService;
private final S3Service s3Service;

public Long processUser(final KakaoUserResponse userResponse) {
if (isExistingUser(userResponse.id())) {
return getIdBySocialId(userResponse.id());
} else {
return createUser(userResponse);
}
}

public Long createUser(final KakaoUserResponse userResponse) {
String email = Optional.ofNullable(userResponse.kakaoAccount())
.map(KakaoAccount::email)
Expand All @@ -53,41 +53,15 @@ public Long createUser(final KakaoUserResponse userResponse) {
}

public Long getIdBySocialId(final Long socialId) {
User user = userRepository.findBySocialId(socialId)
.orElseThrow(() -> new UserNotFoundException(ErrorCode.USER_NOT_FOUND));
return user.getId();
return userRepository.findBySocialId(socialId)
.orElseThrow(() -> new UserNotFoundException(ErrorCode.USER_NOT_FOUND))
.getId();
}

public boolean isExistingUser(final Long socialId) {
return userRepository.findBySocialId(socialId).isPresent();
}

public TokenResponse getTokenByUserId(final Long id) {
UserAuthentication userAuthentication = new UserAuthentication(id, null, null);
String refreshToken = jwtTokenProvider.issueRefreshToken(userAuthentication);
refreshTokenService.saveRefreshToken(id, refreshToken);
return TokenResponse.of(
jwtTokenProvider.issueAccessToken(userAuthentication),
refreshToken
);
}

public TokenResponse reissueToken(final String refreshToken) {
JwtValidationType validationType = jwtTokenProvider.validateToken(refreshToken);
if (validationType != JwtValidationType.VALID_JWT) {
throw new JwtCustomException(ErrorCode.INVALID_JWT_TOKEN);
}

Long userId = jwtTokenProvider.getUserFromJwt(refreshToken);
UserAuthentication userAuthentication = new UserAuthentication(userId, null, null);
String newAccessToken = jwtTokenProvider.issueAccessToken(userAuthentication);
String newRefreshToken = jwtTokenProvider.issueRefreshToken(userAuthentication);

// 새로운 리프레시 토큰으로 교체
refreshTokenService.saveRefreshToken(userId, newRefreshToken);
return TokenResponse.of(newAccessToken, newRefreshToken);
}

public String saveNickname(NicknameRequest nicknameRequest, User user) {
userRepository.findById(user.getId())
.orElseThrow(() -> new UserNotFoundException(ErrorCode.USER_NOT_FOUND));
Expand Down

0 comments on commit e51e62c

Please sign in to comment.