diff --git a/server/src/main/java/sallange/server/auth/application/UserOAuthService.java b/server/src/main/java/sallange/server/auth/application/UserOAuthService.java index fd51ce0..b95069a 100644 --- a/server/src/main/java/sallange/server/auth/application/UserOAuthService.java +++ b/server/src/main/java/sallange/server/auth/application/UserOAuthService.java @@ -5,7 +5,7 @@ import org.springframework.stereotype.Service; import sallange.server.auth.OAuthProvider; import sallange.server.auth.client.OAuthInfoResponse; -import sallange.server.auth.client.UserOAuthClient; +import sallange.server.auth.client.OAuthClient; import sallange.server.auth.util.OAuthLoginParams; import java.util.List; @@ -21,14 +21,14 @@ public class UserOAuthService { private final String baseUri; private final String apiUri; private final String redirectUri; - private final Map clients; + private final Map clients; public UserOAuthService( @Value("${oauth.kakao.client-id}") String clientId, @Value("${oauth.kakao.redirect-uri}") String redirectUri, @Value("${oauth.kakao.url.auth}") String baseUri, @Value("${oauth.kakao.url.api}") String apiUri, - List clients + List clients ) { this.clientId = clientId; this.redirectUri = redirectUri; @@ -36,7 +36,7 @@ public UserOAuthService( this.apiUri = apiUri; this.clients = clients.stream().collect( Collectors.toUnmodifiableMap( - UserOAuthClient::oAuthProvider, + OAuthClient::oAuthProvider, Function.identity() ) ); @@ -51,7 +51,7 @@ public String loginRedirectUri() { } public OAuthInfoResponse request(final OAuthLoginParams params) { - UserOAuthClient client = clients.get(params.oAuthProvider()); + OAuthClient client = clients.get(params.oAuthProvider()); String accessToken = client.requestAccessToken(params); return client.requestOauthInfo(accessToken); diff --git a/server/src/main/java/sallange/server/auth/client/KakaoApiClient.java b/server/src/main/java/sallange/server/auth/client/KakaoApiClient.java new file mode 100644 index 0000000..cef6d32 --- /dev/null +++ b/server/src/main/java/sallange/server/auth/client/KakaoApiClient.java @@ -0,0 +1,84 @@ +package sallange.server.auth.client; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; +import sallange.server.auth.OAuthProvider; +import sallange.server.auth.util.OAuthLoginParams; + +@RequiredArgsConstructor +@Profile("!test") +@Component +public class KakaoApiClient implements OAuthClient { + + private static final String GRANT_TYPE = "authorization_code"; + + @Value("${oauth.kakao.url.auth}") + private String authUrl; + + @Value("${oauth.kakao.url.api}") + private String apiUrl; + + @Value("${oauth.kakao.client-id}") + private String clientId; + + @Value("${oauth.kakao.redirect-uri}") + private String redirectUri; + + private final RestTemplate restTemplate; + private final ObjectMapper objectMapper; + + @Override + public OAuthProvider oAuthProvider() { + return OAuthProvider.KAKAO; + } + + @Override + public String requestAccessToken(final OAuthLoginParams params) { + String url = authUrl + "/oauth/token"; + + final HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + final MultiValueMap body = params.makeBody(); + body.add("grant_type", GRANT_TYPE); + body.add("client_id", clientId); + body.add("redirect_uri", redirectUri); + + final HttpEntity> request = new HttpEntity<>(body, headers); + + KakaoTokens response = restTemplate.postForObject(url, request, KakaoTokens.class); + + assert response != null; + return response.getAccessToken(); + } + + @Override + public OAuthInfoResponse requestOauthInfo(final String accessToken) { + String url = apiUrl + "/v2/user/me"; + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + httpHeaders.setBearerAuth(accessToken); + + HttpEntity request = new HttpEntity<>(null, httpHeaders); + + ResponseEntity response = restTemplate.postForEntity(url, request, String.class); + String responseBody = response.getBody(); + + try { + return objectMapper.readValue(responseBody, KakaoInfoResponse.class); + } catch (JsonProcessingException e) { + throw new RuntimeException("[ERROR] KakaoInfoResponse로 직렬화에 실패했습니다"); + } + } +} diff --git a/server/src/main/java/sallange/server/auth/client/KakaoInfoResponse.java b/server/src/main/java/sallange/server/auth/client/KakaoInfoResponse.java new file mode 100644 index 0000000..c36399a --- /dev/null +++ b/server/src/main/java/sallange/server/auth/client/KakaoInfoResponse.java @@ -0,0 +1,52 @@ +package sallange.server.auth.client; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import sallange.server.auth.OAuthProvider; + +@Getter +@JsonIgnoreProperties(ignoreUnknown = true) +public class KakaoInfoResponse implements OAuthInfoResponse { + + @JsonProperty("id") + private Long id; + + @JsonProperty("kakao_account") + private KakaoAccount kakaoAccount; + + @Getter + @JsonIgnoreProperties(ignoreUnknown = true) + static class KakaoAccount { + + private KakaoProfile profile; + private String email; + } + + @Getter + @JsonIgnoreProperties(ignoreUnknown = true) + static class KakaoProfile { + + private String nickname; + } + + @Override + public String getEmail() { + return kakaoAccount.email; + } + + @Override + public String getNickname() { + return kakaoAccount.profile.nickname; + } + + @Override + public OAuthProvider getOAuthProvider() { + return OAuthProvider.KAKAO; + } + + @Override + public Long getOAuthId() { + return id; + } +} diff --git a/server/src/main/java/sallange/server/auth/client/KakaoTokens.java b/server/src/main/java/sallange/server/auth/client/KakaoTokens.java new file mode 100644 index 0000000..2dc22f2 --- /dev/null +++ b/server/src/main/java/sallange/server/auth/client/KakaoTokens.java @@ -0,0 +1,28 @@ +package sallange.server.auth.client; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class KakaoTokens { + + @JsonProperty("access_token") + private String accessToken; + + @JsonProperty("token_type") + private String tokenType; + + @JsonProperty("refresh_token") + private String refreshToken; + + @JsonProperty("expires_in") + private String expiresIn; + + @JsonProperty("refresh_token_expires_in") + private String refreshTokenExpiresIn; + + @JsonProperty("scope") + private String scope; +} diff --git a/server/src/main/java/sallange/server/auth/client/UserOAuthClient.java b/server/src/main/java/sallange/server/auth/client/OAuthClient.java similarity index 89% rename from server/src/main/java/sallange/server/auth/client/UserOAuthClient.java rename to server/src/main/java/sallange/server/auth/client/OAuthClient.java index 0e93eb4..32a8e15 100644 --- a/server/src/main/java/sallange/server/auth/client/UserOAuthClient.java +++ b/server/src/main/java/sallange/server/auth/client/OAuthClient.java @@ -3,7 +3,7 @@ import sallange.server.auth.OAuthProvider; import sallange.server.auth.util.OAuthLoginParams; -public interface UserOAuthClient { +public interface OAuthClient { OAuthProvider oAuthProvider(); diff --git a/server/src/main/java/sallange/server/config/OAuthClientConfig.java b/server/src/main/java/sallange/server/config/OAuthClientConfig.java new file mode 100644 index 0000000..a2d4eb8 --- /dev/null +++ b/server/src/main/java/sallange/server/config/OAuthClientConfig.java @@ -0,0 +1,14 @@ +package sallange.server.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class OAuthClientConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +}