diff --git a/src/main/java/com/example/egobook_be/domain/auth/enums/AuthErrorCode.java b/src/main/java/com/example/egobook_be/domain/auth/enums/AuthErrorCode.java index aa6cf71..3792d84 100644 --- a/src/main/java/com/example/egobook_be/domain/auth/enums/AuthErrorCode.java +++ b/src/main/java/com/example/egobook_be/domain/auth/enums/AuthErrorCode.java @@ -30,6 +30,7 @@ public enum AuthErrorCode implements BaseErrorCode { */ RECERTIFICATION_FAIL_USER_WITHDRAW_PENDING(HttpStatus.FORBIDDEN, "사용자가 탈퇴 대기중이기 때문에, 해당 토큰으로 재인증할 수 없습니다."), RECERTIFICATION_FAIL_USER_WITHDRAWN(HttpStatus.FORBIDDEN, "사용자가 탈퇴했기 때문에, 해당 토큰으로 재인증할 수 없습니다."), + GOOGLE_JOIN_FAIL_USER_WITHDRAWN(HttpStatus.FORBIDDEN, "사용자가 탈퇴했기 때문에, 구글로 회원가입 할 수 없습니다."), /** * 401 UNAUTHORIZED: 인증되지 않음 diff --git a/src/main/java/com/example/egobook_be/domain/auth/repository/AuthAccountRepository.java b/src/main/java/com/example/egobook_be/domain/auth/repository/AuthAccountRepository.java index c698379..643b387 100644 --- a/src/main/java/com/example/egobook_be/domain/auth/repository/AuthAccountRepository.java +++ b/src/main/java/com/example/egobook_be/domain/auth/repository/AuthAccountRepository.java @@ -9,7 +9,6 @@ import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import java.util.Collection; import java.util.List; import java.util.Optional; @@ -24,20 +23,10 @@ public interface AuthAccountRepository extends JpaRepository */ @Query("SELECT a FROM AuthAccount a JOIN FETCH a.user " + "WHERE a.hashedDeviceUid = :hashedDeviceUid AND a.provider = :provider") - Optional findByDeviceUidAndProvider( + Optional findByHashedDeviceUidAndProvider( @Param("hashedDeviceUid") String hashedDeviceUid, @Param("provider") Provider provider ); - /** - * HashedDeviceUid & Provider로 AuthAccount 객체를 찾는 함수 - * @param hashedDeviceUid String - * @param provider Provider - * @return Optional - */ - @Query("select a from AuthAccount a " + - "join fetch a.user u " + - "where a.hashedDeviceUid = :hashedDeviceUid and a.provider = :provider") - Optional findByHashedDeviceUidAndProvider(@Param("hashedDeviceUid") String hashedDeviceUid, @Param("provider") Provider provider); /** * 특정 유저의 특정 Provider 계정 조회 (Guest 계정 찾기용) */ diff --git a/src/main/java/com/example/egobook_be/domain/auth/sevice/AuthService.java b/src/main/java/com/example/egobook_be/domain/auth/sevice/AuthService.java index 0f23e85..4da0e24 100644 --- a/src/main/java/com/example/egobook_be/domain/auth/sevice/AuthService.java +++ b/src/main/java/com/example/egobook_be/domain/auth/sevice/AuthService.java @@ -16,6 +16,7 @@ import com.example.egobook_be.domain.terms.repository.UserTermRepository; import com.example.egobook_be.domain.user.entity.Ability; import com.example.egobook_be.domain.user.enums.RoleType; +import com.example.egobook_be.domain.user.enums.UserErrorCode; import com.example.egobook_be.domain.user.enums.UserStatus; import com.example.egobook_be.domain.user.repository.AbilityRepository; import com.example.egobook_be.global.util.*; @@ -99,8 +100,22 @@ public JwtTokenResDto registerGoogle(GoogleJoinReqDto reqDto){ * - Provider.GOOGLE과 조합하여 체크한다. */ String hashedGoogleSub = hashingUtil.hashingValue(googleSub); - if(authAccountRepository.existsByHashedDeviceUidAndProvider(hashedGoogleSub, Provider.GOOGLE)){ - throw new CustomException(AuthErrorCode.ALREADY_REGISTERED_USER); + AuthAccount authAccount = authAccountRepository.findByHashedDeviceUidAndProvider(hashedGoogleSub, Provider.GOOGLE).orElse(null); + if(authAccount != null){ + // (1) 기존에 가입된 계정이라면, User를 찾아서 삭제 대기중인지 확인한다 + User user = authAccount.getUser(); + + // (2) 만약 삭제 대기 중인 경우라면 해당 계정의 상태를 ACTIVE로 변경하고, 토큰을 새로 발급한 뒤 Refresh Token Backup 테이블 & Redis 갱신 후 결과 반환 + if(user.getStatus().equals(UserStatus.WITHDRAW_PENDING)){ + log.info("[Google Join] 탈퇴 대기 중인 구글 사용자가 재 회원가입을 시도하였습니다."); + user.cancelWithDrawUser(); // 사용자 상태 복구 + return processIssueTokens(user, authAccount, email); + } + // (3) 만약 사용자가 삭제 상태면 예외 throw + if(user.getStatus().equals(UserStatus.WITHDRAW)){ + log.info("[Google Join] 탈퇴된 구글 사용자가 재 회원가입을 시도하였습니다."); + throw new CustomException(AuthErrorCode.GOOGLE_JOIN_FAIL_USER_WITHDRAWN); + } } /* @@ -116,24 +131,29 @@ public JwtTokenResDto registerGoogle(GoogleJoinReqDto reqDto){ * - hashedDeviceUid 자리에 hashedGoogleSub를 저장한다. * - recoverToken은 createAuthAccount 내부에서 초기값(null)으로 설정된다. */ - AuthAccount authAccount = createAuthAccount(user, Provider.GOOGLE, hashedGoogleSub); + authAccount = createAuthAccount(user, Provider.GOOGLE, hashedGoogleSub); + // 5. Token을 발급 및 환경 세팅 수행 + return processIssueTokens(user, authAccount, email); + } - // 5. 토큰 발급을 위한 UserDetails 생성 + /** Token들을 발급하는 과정을 담은 함수 */ + private JwtTokenResDto processIssueTokens(User user, AuthAccount authAccount, String email){ + // 1. 토큰 발급을 위한 UserDetails 생성 CustomUserDetails userDetails = buildCustomUserDetails(user, authAccount); /* - * 6. Access, Refresh Token 생성 + * 2. Access, Refresh Token 생성 * - **주의**: Google은 Recover Token을 생성하지 않는다. */ TokenInfo accessTokenInfo = jwtUtil.createAccessToken(userDetails); TokenInfo refreshTokenInfo = jwtUtil.createRefreshToken(userDetails); - // 7. Refresh Token을 Table, Redis에 저장하는 Process 수행 + // 3. Refresh Token을 Table, Redis에 저장하는 Process 수행 processRefreshTokenSaving(user, authAccount, refreshTokenInfo); /* - * 8. 클라이언트에게 토큰 반환 + * 4. 클라이언트에게 토큰 반환 * - Google 로그인이므로 recoverToken은 null을 반환한다. */ return buildJwtTokenResDto(accessTokenInfo.token(), refreshTokenInfo.token(), null, email); diff --git a/src/main/java/com/example/egobook_be/domain/user/entity/User.java b/src/main/java/com/example/egobook_be/domain/user/entity/User.java index 968a5b0..74b4c0e 100644 --- a/src/main/java/com/example/egobook_be/domain/user/entity/User.java +++ b/src/main/java/com/example/egobook_be/domain/user/entity/User.java @@ -153,6 +153,14 @@ public void withdrawUser(Long purgeDurationInMs) { this.notificationEnabled = false; } + public void cancelWithDrawUser(){ + this.status = UserStatus.ACTIVE; + this.deletedAt = null; + this.purgeAt = null; + this.dailyPraise = true; + this.notificationEnabled = true; + } + public void addInk(int amount) { this.ink += amount; } diff --git a/src/main/java/com/example/egobook_be/domain/user/repository/UserRepository.java b/src/main/java/com/example/egobook_be/domain/user/repository/UserRepository.java index 73338d8..87ca513 100644 --- a/src/main/java/com/example/egobook_be/domain/user/repository/UserRepository.java +++ b/src/main/java/com/example/egobook_be/domain/user/repository/UserRepository.java @@ -74,4 +74,5 @@ List findByNicknameContainingIgnoreCaseOrAccountCodeContainingIgnoreCase( """) List findAvailableReceivers(@Param("now") OffsetDateTime now, Pageable pageable); + Optional findByEmail(String email); } diff --git a/src/main/java/com/example/egobook_be/global/loader/ItemInitializer.java b/src/main/java/com/example/egobook_be/global/loader/ItemInitializer.java index 2de6798..26296c0 100644 --- a/src/main/java/com/example/egobook_be/global/loader/ItemInitializer.java +++ b/src/main/java/com/example/egobook_be/global/loader/ItemInitializer.java @@ -167,8 +167,8 @@ private List getInitItemList(){ * 5-3) Beach.png */ items.add(buildBackgroundItem("Default.png", 0)); - items.add(buildBackgroundItem("Blossom.png", 0)); - items.add(buildBackgroundItem("Beach.png", 0)); + items.add(buildBackgroundItem("Blossom.png", 750)); + items.add(buildBackgroundItem("Beach.png", 1000)); return items; } diff --git a/src/main/java/com/example/egobook_be/global/security/CustomUserDetailService.java b/src/main/java/com/example/egobook_be/global/security/CustomUserDetailService.java index 39a2a1d..6ab2cd9 100644 --- a/src/main/java/com/example/egobook_be/global/security/CustomUserDetailService.java +++ b/src/main/java/com/example/egobook_be/global/security/CustomUserDetailService.java @@ -64,7 +64,7 @@ public UserDetails loadUserByUsername(String compositeKey) { * - AuthAccountRepository에서 fetch join으로 영속성 컨텍스트에 User 정보까지 같이 가져온 상태이다. * throw 해당 UID 기기를 찾을 수 없다는 예외 */ - AuthAccount authAccount = authAccountRepository.findByDeviceUidAndProvider(deviceUid, provider) + AuthAccount authAccount = authAccountRepository.findByHashedDeviceUidAndProvider(deviceUid, provider) .orElseThrow(() -> new CustomException(AuthErrorCode.USER_NOT_FOUND)); /*