Skip to content

Commit b4c6ae3

Browse files
unam98alh0409
andauthored
fix: 카카오/애플 외부 API 호출에 타임아웃 명시 (3초) (#243)
RestTemplate(카카오)과 OkHttpClient(애플, 기본 생성자)에 타임아웃이 없어서, 외부 서비스가 응답을 늦게 주면 그 요청을 처리하던 톰캣 스레드가 무제한 대기하게 되는 문제가 있었다. 스레드가 이런 식으로 계속 묶이면(누수) 카카오/애플 로그인과 무관한 다른 API 요청까지 스레드 풀 고갈로 영향을 받을 수 있다. - KakaoSignInService: RestTemplate에 SimpleClientHttpRequestFactory로 connect/read timeout 3초 설정 - AppleSignInService: JWKS 조회(RemoteJWKSet)에 DefaultResourceRetriever로 타임아웃 지정, 회원탈퇴 통보(OkHttpClient)에도 동일하게 적용 Google은 google-http-client 자체 기본 타임아웃(20초)이 이미 있어 이번 변경에서 제외. Co-authored-by: 나미 <dnska6657@gmail.com>
1 parent d7e6d11 commit b4c6ae3

2 files changed

Lines changed: 28 additions & 3 deletions

File tree

src/main/java/org/runnect/server/auth/service/AppleSignInService.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.nimbusds.jose.jwk.source.RemoteJWKSet;
77
import com.nimbusds.jose.proc.JWSVerificationKeySelector;
88
import com.nimbusds.jose.proc.SecurityContext;
9+
import com.nimbusds.jose.util.DefaultResourceRetriever;
910
import com.nimbusds.jwt.JWTClaimsSet;
1011
import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
1112
import com.nimbusds.jwt.proc.DefaultJWTProcessor;
@@ -52,6 +53,10 @@ public class AppleSignInService {
5253
@Value("${apple.revoke-url}")
5354
private String APPLE_REVOKE_URL;
5455
private static final String APPLE_JWKS_URL = "https://appleid.apple.com/auth/keys";
56+
// 애플 공개키(JWKS) 조회 및 회원탈퇴 통보에 타임아웃을 명시한다. 기본값(무제한 대기)이면
57+
// 애플 쪽이 응답을 늦게 줄 때 이 요청을 처리하던 톰캣 스레드가 계속 묶여있게 된다.
58+
private static final int CONNECT_TIMEOUT_MS = 3000;
59+
private static final int READ_TIMEOUT_MS = 3000;
5560

5661
private PrivateKey PRIVATE_KEY;
5762
@Value("${apple.p8key}")
@@ -109,7 +114,9 @@ public SocialInfoResponseDto getSocialInfo(String idToken) {
109114
}
110115

111116
private JWTClaimsSet verifySignatureAndGetClaims(String idToken) throws Exception {
112-
JWKSource<SecurityContext> keySource = new RemoteJWKSet<>(new URL(APPLE_JWKS_URL));
117+
JWKSource<SecurityContext> keySource = new RemoteJWKSet<>(
118+
new URL(APPLE_JWKS_URL),
119+
new DefaultResourceRetriever(CONNECT_TIMEOUT_MS, READ_TIMEOUT_MS));
113120
ConfigurableJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
114121
JWSVerificationKeySelector<SecurityContext> keySelector =
115122
new JWSVerificationKeySelector<>(JWSAlgorithm.RS256, keySource);
@@ -137,7 +144,11 @@ public void reportWithdrawalToApple(String appleAccessToken) {
137144

138145
String clientSecret = createClientSecret();
139146

140-
OkHttpClient client = new OkHttpClient();
147+
OkHttpClient client = new OkHttpClient.Builder()
148+
.connectTimeout(CONNECT_TIMEOUT_MS, java.util.concurrent.TimeUnit.MILLISECONDS)
149+
.readTimeout(READ_TIMEOUT_MS, java.util.concurrent.TimeUnit.MILLISECONDS)
150+
.writeTimeout(READ_TIMEOUT_MS, java.util.concurrent.TimeUnit.MILLISECONDS)
151+
.build();
141152

142153
RequestBody formBody = new FormBody.Builder()
143154
.add("token", appleAccessToken)

src/main/java/org/runnect/server/auth/service/KakaoSignInService.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.springframework.http.HttpHeaders;
1313
import org.springframework.http.HttpMethod;
1414
import org.springframework.http.ResponseEntity;
15+
import org.springframework.http.client.SimpleClientHttpRequestFactory;
1516
import org.springframework.stereotype.Service;
1617
import org.springframework.util.MultiValueMap;
1718
import org.springframework.web.client.RestTemplate;
@@ -21,14 +22,20 @@
2122
@RequiredArgsConstructor
2223
public class KakaoSignInService {
2324

25+
// 기본 RestTemplate은 타임아웃이 없어(무제한 대기), 카카오가 응답을 늦게 주면
26+
// 그 요청을 처리하던 톰캣 스레드가 계속 묶여있게 된다. 스레드 풀이 이런 식으로
27+
// 소진되면 카카오 로그인과 무관한 다른 API 요청까지 영향을 받는다.
28+
private static final int CONNECT_TIMEOUT_MS = 3000;
29+
private static final int READ_TIMEOUT_MS = 3000;
30+
2431
public SocialInfoResponseDto getSocialInfo(String token) {
2532

2633
HttpHeaders headers = new HttpHeaders();
2734
headers.add("Authorization", "Bearer " + token);
2835
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");
2936

3037
HttpEntity<MultiValueMap<String, String>> kakaoUserInfoRequest = new HttpEntity<>(headers);
31-
RestTemplate rt = new RestTemplate();
38+
RestTemplate rt = new RestTemplate(createTimeoutRequestFactory());
3239

3340
String userId = null;
3441
String email = null;
@@ -61,4 +68,11 @@ public SocialInfoResponseDto getSocialInfo(String token) {
6168
}
6269
return SocialInfoResponseDto.of(email, userId);
6370
}
71+
72+
private SimpleClientHttpRequestFactory createTimeoutRequestFactory() {
73+
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
74+
factory.setConnectTimeout(CONNECT_TIMEOUT_MS);
75+
factory.setReadTimeout(READ_TIMEOUT_MS);
76+
return factory;
77+
}
6478
}

0 commit comments

Comments
 (0)