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 @@ -4,10 +4,13 @@

/**
* 카카오 로그인 요청 DTO
* - authorizationCode : 카카오 인가 코드 (authorization_code)
* Android 카카오 SDK에서 발급받은 accessToken, idToken을 전달합니다.
* idToken은 선택값이며 현재 서버에서는 accessToken을 기반으로 카카오 사용자 정보를 조회합니다.
*/
public record KakaoLoginRequest(
@NotBlank(message = "카카오 인가 코드는 필수입니다.")
String authorizationCode
@NotBlank(message = "카카오 access token은 필수입니다.")
String accessToken,
String idToken
) {
}

Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ public class KakaoLoginUseCase {

@Transactional
public KakaoLoginResponse execute(KakaoLoginRequest request) {
KakaoUserInfoResponse userInfo = kakaoAuthService.getUserInfoByAuthorizationCode(
request.authorizationCode()
KakaoUserInfoResponse userInfo = kakaoAuthService.getUserInfoByAccessToken(
request.accessToken()
);

String email = userInfo.getEmail();
Expand Down Expand Up @@ -70,3 +70,4 @@ public KakaoLoginResponse execute(KakaoLoginRequest request) {
);
}
}

42 changes: 37 additions & 5 deletions src/main/java/plango/auth/domain/service/KakaoAuthService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package plango.auth.domain.service;

import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -19,15 +20,12 @@
import plango.global.common.exception.BusinessException;
import plango.global.common.exception.ErrorCode;

import jakarta.annotation.PostConstruct;

@Slf4j
@Service
@RequiredArgsConstructor
public class KakaoAuthService {

private static final String KAKAO_TOKEN_URI = "https://kauth.kakao.com/oauth/token";

private static final String KAKAO_USER_INFO_URI = "https://kapi.kakao.com/v2/user/me";

@Value("${security.oauth2.kakao.client-id}")
Expand All @@ -45,57 +43,80 @@ public void printConfig() {
log.info("redirectUri = {}", redirectUri);
}

/**
* (웹용) 인가 코드를 이용해 카카오 사용자 정보를 조회하는 메서드
*/
public KakaoUserInfoResponse getUserInfoByAuthorizationCode(String authorizationCode) {
KakaoTokenResponse tokenResponse = requestToken(authorizationCode);
String accessToken = tokenResponse.accessToken();
log.info("accessToken from Kakao = {}", accessToken);
return requestUserInfo(accessToken);
}

/**
* (모바일용) 이미 발급받은 accessToken 으로 바로 사용자 정보 조회
*/
public KakaoUserInfoResponse getUserInfoByAccessToken(String accessToken) {
return requestUserInfo(accessToken);
}

private KakaoTokenResponse requestToken(String authorizationCode) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
body.add("grant_type", "authorization_code");
body.add("client_id", clientId);
body.add("redirect_uri", redirectUri);
body.add("code", authorizationCode);

log.info("requestToken() body = {}", body);

HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(body, headers);

try {
ResponseEntity<KakaoTokenResponse> response = restTemplate.postForEntity(
KAKAO_TOKEN_URI,
entity,
KakaoTokenResponse.class
);

log.info("requestToken() status = {}", response.getStatusCode());

KakaoTokenResponse tokenResponse = response.getBody();

if (!response.getStatusCode().is2xxSuccessful() || tokenResponse == null) {
throw new BusinessException(
ErrorCode.KAKAO_SERVER_ERROR.getStatusCode(),
ErrorCode.KAKAO_SERVER_ERROR.getMessage()
);
}

if (tokenResponse.accessToken() == null || tokenResponse.accessToken().isBlank()) {
log.error("Kakao token response has no access_token. body = {}", tokenResponse);
throw new BusinessException(
ErrorCode.KAKAO_SERVER_ERROR.getStatusCode(),
ErrorCode.KAKAO_SERVER_ERROR.getMessage()
);
}

return tokenResponse;
} catch (RestClientResponseException exception) {
log.error("Kakao token request error. status = {}, body = {}",
log.error(
"Kakao token request error. status = {}, body = {}",
exception.getRawStatusCode(),
exception.getResponseBodyAsString()
);

ErrorCode errorCode = selectErrorCode(exception.getRawStatusCode());

throw new BusinessException(
errorCode.getStatusCode(),
errorCode.getMessage()
);
} catch (RestClientException exception) {
log.error("Kakao token request RestClientException", exception);

throw new BusinessException(
ErrorCode.KAKAO_SERVER_ERROR.getStatusCode(),
ErrorCode.KAKAO_SERVER_ERROR.getMessage()
Expand All @@ -106,34 +127,43 @@ private KakaoTokenResponse requestToken(String authorizationCode) {
private KakaoUserInfoResponse requestUserInfo(String accessToken) {
HttpHeaders headers = createBearerHeaders(accessToken);
HttpEntity<Void> entity = new HttpEntity<>(headers);

try {
ResponseEntity<KakaoUserInfoResponse> response = restTemplate.exchange(
KAKAO_USER_INFO_URI,
HttpMethod.GET,
entity,
KakaoUserInfoResponse.class
);

log.info("requestUserInfo() status = {}", response.getStatusCode());

KakaoUserInfoResponse userInfo = response.getBody();

if (!response.getStatusCode().is2xxSuccessful() || userInfo == null) {
throw new BusinessException(
ErrorCode.KAKAO_SERVER_ERROR.getStatusCode(),
ErrorCode.KAKAO_SERVER_ERROR.getMessage()
);
}

return userInfo;
} catch (RestClientResponseException exception) {
log.error("Kakao userInfo request error. status = {}, body = {}",
log.error(
"Kakao userInfo request error. status = {}, body = {}",
exception.getRawStatusCode(),
exception.getResponseBodyAsString()
);

ErrorCode errorCode = selectErrorCode(exception.getRawStatusCode());

throw new BusinessException(
errorCode.getStatusCode(),
errorCode.getMessage()
);
} catch (RestClientException exception) {
log.error("Kakao userInfo request RestClientException", exception);

throw new BusinessException(
ErrorCode.KAKAO_SERVER_ERROR.getStatusCode(),
ErrorCode.KAKAO_SERVER_ERROR.getMessage()
Expand All @@ -152,6 +182,8 @@ private ErrorCode selectErrorCode(int statusCode) {
if (statusCode == 401) {
return ErrorCode.KAKAO_INVALID_TOKEN;
}

return ErrorCode.KAKAO_SERVER_ERROR;
}
}

5 changes: 4 additions & 1 deletion src/main/java/plango/auth/presentation/AuthController.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ public ResponseEntity<CommonResponse<KakaoLoginResponse>> login(
);
}

@Operation(summary = "카카오 로그인", description = "카카오 인가 코드를 사용해 로그인합니다.")
@Operation(
summary = "카카오 로그인",
description = "Android 카카오 SDK에서 발급받은 accessToken을 사용해 로그인합니다."
)
@PostMapping("/login/kakao")
public ResponseEntity<CommonResponse<KakaoLoginResponse>> kakaoLogin(
@Valid
Expand Down