diff --git a/src/main/java/org/runnect/server/auth/service/AppleSignInService.java b/src/main/java/org/runnect/server/auth/service/AppleSignInService.java index 706149f..a349ff1 100644 --- a/src/main/java/org/runnect/server/auth/service/AppleSignInService.java +++ b/src/main/java/org/runnect/server/auth/service/AppleSignInService.java @@ -6,6 +6,7 @@ import com.nimbusds.jose.jwk.source.RemoteJWKSet; import com.nimbusds.jose.proc.JWSVerificationKeySelector; import com.nimbusds.jose.proc.SecurityContext; +import com.nimbusds.jose.util.DefaultResourceRetriever; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.proc.ConfigurableJWTProcessor; import com.nimbusds.jwt.proc.DefaultJWTProcessor; @@ -52,6 +53,10 @@ public class AppleSignInService { @Value("${apple.revoke-url}") private String APPLE_REVOKE_URL; private static final String APPLE_JWKS_URL = "https://appleid.apple.com/auth/keys"; + // 애플 공개키(JWKS) 조회 및 회원탈퇴 통보에 타임아웃을 명시한다. 기본값(무제한 대기)이면 + // 애플 쪽이 응답을 늦게 줄 때 이 요청을 처리하던 톰캣 스레드가 계속 묶여있게 된다. + private static final int CONNECT_TIMEOUT_MS = 3000; + private static final int READ_TIMEOUT_MS = 3000; private PrivateKey PRIVATE_KEY; @Value("${apple.p8key}") @@ -109,7 +114,9 @@ public SocialInfoResponseDto getSocialInfo(String idToken) { } private JWTClaimsSet verifySignatureAndGetClaims(String idToken) throws Exception { - JWKSource keySource = new RemoteJWKSet<>(new URL(APPLE_JWKS_URL)); + JWKSource keySource = new RemoteJWKSet<>( + new URL(APPLE_JWKS_URL), + new DefaultResourceRetriever(CONNECT_TIMEOUT_MS, READ_TIMEOUT_MS)); ConfigurableJWTProcessor jwtProcessor = new DefaultJWTProcessor<>(); JWSVerificationKeySelector keySelector = new JWSVerificationKeySelector<>(JWSAlgorithm.RS256, keySource); @@ -137,7 +144,11 @@ public void reportWithdrawalToApple(String appleAccessToken) { String clientSecret = createClientSecret(); - OkHttpClient client = new OkHttpClient(); + OkHttpClient client = new OkHttpClient.Builder() + .connectTimeout(CONNECT_TIMEOUT_MS, java.util.concurrent.TimeUnit.MILLISECONDS) + .readTimeout(READ_TIMEOUT_MS, java.util.concurrent.TimeUnit.MILLISECONDS) + .writeTimeout(READ_TIMEOUT_MS, java.util.concurrent.TimeUnit.MILLISECONDS) + .build(); RequestBody formBody = new FormBody.Builder() .add("token", appleAccessToken) diff --git a/src/main/java/org/runnect/server/auth/service/KakaoSignInService.java b/src/main/java/org/runnect/server/auth/service/KakaoSignInService.java index a87d904..f55389a 100644 --- a/src/main/java/org/runnect/server/auth/service/KakaoSignInService.java +++ b/src/main/java/org/runnect/server/auth/service/KakaoSignInService.java @@ -12,6 +12,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.stereotype.Service; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; @@ -21,6 +22,12 @@ @RequiredArgsConstructor public class KakaoSignInService { + // 기본 RestTemplate은 타임아웃이 없어(무제한 대기), 카카오가 응답을 늦게 주면 + // 그 요청을 처리하던 톰캣 스레드가 계속 묶여있게 된다. 스레드 풀이 이런 식으로 + // 소진되면 카카오 로그인과 무관한 다른 API 요청까지 영향을 받는다. + private static final int CONNECT_TIMEOUT_MS = 3000; + private static final int READ_TIMEOUT_MS = 3000; + public SocialInfoResponseDto getSocialInfo(String token) { HttpHeaders headers = new HttpHeaders(); @@ -28,7 +35,7 @@ public SocialInfoResponseDto getSocialInfo(String token) { headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8"); HttpEntity> kakaoUserInfoRequest = new HttpEntity<>(headers); - RestTemplate rt = new RestTemplate(); + RestTemplate rt = new RestTemplate(createTimeoutRequestFactory()); String userId = null; String email = null; @@ -61,4 +68,11 @@ public SocialInfoResponseDto getSocialInfo(String token) { } return SocialInfoResponseDto.of(email, userId); } + + private SimpleClientHttpRequestFactory createTimeoutRequestFactory() { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setConnectTimeout(CONNECT_TIMEOUT_MS); + factory.setReadTimeout(READ_TIMEOUT_MS); + return factory; + } }