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 @@ -6,6 +6,7 @@
import devkor.ontime_back.dto.OAuthKakaoUserDto;
import devkor.ontime_back.global.oauth.apple.AppleLoginService;
import devkor.ontime_back.global.oauth.google.GoogleLoginService;
import devkor.ontime_back.response.ApiResponseForm;
import devkor.ontime_back.service.UserAuthService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
Expand All @@ -16,6 +17,7 @@
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Slf4j
Expand Down Expand Up @@ -113,24 +115,32 @@ public String appleRegisterOrLogin(@RequestBody OAuthAppleRequestDto appleLoginR
summary = "애플 소셜 로그인 회원탈퇴"
)
@DeleteMapping("/apple/me")
public String appleDeleteUser(HttpServletRequest request, HttpServletResponse response, @RequestBody(required = false) FeedbackAddDto feedbackAddDto) throws Exception {
public ResponseEntity<ApiResponseForm<?>> appleDeleteUser(HttpServletRequest request, HttpServletResponse response, @RequestBody(required = false) FeedbackAddDto feedbackAddDto) {
Long userId = userAuthService.getUserIdFromToken(request);
log.info("userId: {}", userId);
appleLoginService.revokeToken(userId);
try {
appleLoginService.revokeToken(userId);
} catch (Exception e) {
log.warn("Apple 토큰 철회에 실패했지만 계정 삭제를 계속 진행합니다. userId={}, reason={}", userId, e.getMessage());
}
userAuthService.deleteUser(userId, feedbackAddDto);
return "애플 로그인 회원탈퇴 성공";
return ResponseEntity.ok(ApiResponseForm.success(null, "애플 로그인 회원탈퇴 성공"));
}

@Operation(
summary = "구글 소셜 로그인 회원탈퇴"
)
@DeleteMapping("/google/me")
public String googleDeleteUser(HttpServletRequest request, HttpServletResponse response, @RequestBody(required = false) FeedbackAddDto feedbackAddDto) throws Exception {
public ResponseEntity<ApiResponseForm<?>> googleDeleteUser(HttpServletRequest request, HttpServletResponse response, @RequestBody(required = false) FeedbackAddDto feedbackAddDto) {
Long userId = userAuthService.getUserIdFromToken(request);
log.info("userId: {}", userId);
googleLoginService.revokeToken(userId);
try {
googleLoginService.revokeToken(userId);
} catch (Exception e) {
log.warn("Google 토큰 철회에 실패했지만 계정 삭제를 계속 진행합니다. userId={}, reason={}", userId, e.getMessage());
}
userAuthService.deleteUser(userId, feedbackAddDto);
return "구글 로그인 회원탈퇴 성공";
return ResponseEntity.ok(ApiResponseForm.success(null, "구글 로그인 회원탈퇴 성공"));
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
Expand Down Expand Up @@ -48,13 +49,26 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ

Object loginLock = LOGIN_LOCKS.computeIfAbsent(googleUserId, key -> new Object());
synchronized (loginLock) {
Optional<User> existingUser = userRepository.findBySocialTypeAndSocialId(SocialType.GOOGLE, googleUserId);

if (existingUser.isPresent()) {
return googleLoginService.handleLogin(oAuthGoogleRequestDto, existingUser.get(), response);
List<User> existingUsers = userRepository.findAllBySocialTypeAndSocialIdOrderByIdDesc(SocialType.GOOGLE, googleUserId);

if (!existingUsers.isEmpty()) {
if (existingUsers.size() > 1) {
log.warn("동일한 Google socialId를 가진 유저가 {}명 존재합니다. 최신 userId={} 계정으로 로그인합니다.",
existingUsers.size(), existingUsers.get(0).getId());
}
return googleLoginService.handleLogin(oAuthGoogleRequestDto, existingUsers.get(0), response);
} else {
OAuthGoogleUserDto oAuthGoogleUserDto = new OAuthGoogleUserDto(googleUserId, (String) googlePayload.get("name"), (String) googlePayload.get("picture"), googlePayload.getEmail());
return googleLoginService.handleRegister(oAuthGoogleRequestDto, oAuthGoogleUserDto, response);
try {
return googleLoginService.handleRegister(oAuthGoogleRequestDto, oAuthGoogleUserDto, response);
} catch (DataIntegrityViolationException e) {
log.warn("Google 회원가입 중 중복 socialId가 감지되어 기존 계정으로 로그인합니다. socialId={}", googleUserId);
User user = userRepository.findAllBySocialTypeAndSocialIdOrderByIdDesc(SocialType.GOOGLE, googleUserId)
.stream()
.findFirst()
.orElseThrow(() -> e);
return googleLoginService.handleLogin(oAuthGoogleRequestDto, user, response);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
Expand Down Expand Up @@ -175,7 +176,10 @@ public boolean revokeToken(Long userId) {

String googleRefreshToken = user.getSocialLoginToken();

RestTemplate restTemplate = new RestTemplate();
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(3000);
requestFactory.setReadTimeout(3000);
RestTemplate restTemplate = new RestTemplate(requestFactory);
String revokeUrl = GOOGLE_REVOKE_URL + googleRefreshToken;

HttpHeaders headers = new HttpHeaders();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
Expand All @@ -21,8 +22,10 @@ public interface UserRepository extends JpaRepository<User, Long> {
// 추가정보 입력받을때 사용
Optional<User> findBySocialTypeAndSocialId(SocialType socialType, String socialId);

List<User> findAllBySocialTypeAndSocialIdOrderByIdDesc(SocialType socialType, String socialId);

@Query("SELECT u.spareTime FROM User u WHERE u.id = :id")
Integer findSpareTimeById(Long id);

Optional<Object> findByAccessToken(String token);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
DELETE u
FROM user u
JOIN (
SELECT * FROM (
SELECT social_type, social_id, MAX(user_id) AS keep_user_id
FROM user
WHERE social_type IS NOT NULL
AND social_id IS NOT NULL
GROUP BY social_type, social_id
HAVING COUNT(*) > 1
) duplicate_groups
) d
ON u.social_type = d.social_type
AND u.social_id = d.social_id
WHERE u.user_id <> d.keep_user_id;

SET @constraint_exists = (
SELECT COUNT(*)
FROM information_schema.table_constraints
WHERE constraint_schema = DATABASE()
AND table_name = 'user'
AND constraint_name = 'uk_user_social_type_social_id'
);

SET @add_constraint_sql = IF(
@constraint_exists = 0,
'ALTER TABLE user ADD CONSTRAINT uk_user_social_type_social_id UNIQUE (social_type, social_id)',
'SELECT 1'
);

PREPARE add_constraint_statement FROM @add_constraint_sql;
EXECUTE add_constraint_statement;
DEALLOCATE PREPARE add_constraint_statement;
Loading