-
Notifications
You must be signed in to change notification settings - Fork 0
#13 - 카카오, 애플 소셜로그인 구현 #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
87675db
c6d2784
fba6965
8e4501a
e1a528a
530ca34
c5a2357
067b593
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.wespot.config | ||
|
||
import org.springframework.context.annotation.Configuration | ||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing | ||
|
||
@Configuration | ||
@EnableJpaAuditing | ||
class JpaAuditingConfig { | ||
} | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.wespot.config | ||
|
||
import com.wespot.error.CustomErrorDecoder | ||
import feign.codec.ErrorDecoder | ||
import org.springframework.boot.web.client.RestTemplateBuilder | ||
import org.springframework.cloud.openfeign.EnableFeignClients | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.context.annotation.Configuration | ||
import org.springframework.http.converter.HttpMessageConverter | ||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter | ||
import org.springframework.web.client.RestTemplate | ||
|
||
|
||
@Configuration | ||
@EnableFeignClients(basePackages = ["com.wespot"]) | ||
class OpenFeignConfig { | ||
|
||
@Bean | ||
fun errorDecoder(): ErrorDecoder { | ||
return CustomErrorDecoder() | ||
} | ||
|
||
@Bean | ||
fun feignMessageConverter(): MappingJackson2HttpMessageConverter { | ||
return MappingJackson2HttpMessageConverter() | ||
} | ||
|
||
@Bean | ||
fun restTemplate(messageConverters: List<HttpMessageConverter<*>>): RestTemplate { | ||
return RestTemplateBuilder().additionalMessageConverters(messageConverters).build() | ||
} | ||
Comment on lines
+18
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오호라, errorDecoder이외의 Converter, restTemplate은 꼭 필요한 설정인 것인가요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이전에 로그상의 문제가 생겨서 작성했었는데,, 기억이 가물가물🥲 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 근데 사실 뺄 필요는 없어요. 근거가 있는 것인지! 여쭤봤던거에요 호호호호 |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,16 +31,15 @@ allprojects { | |
} | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
subprojects { | ||
apply(plugin = "java") | ||
|
||
apply(plugin = "kotlin") | ||
apply(plugin = "kotlin-spring") | ||
apply(plugin = "kotlin-kapt") | ||
apply(plugin = "kotlin-noarg") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이놈짜슥.. 이거였구만 |
||
apply(plugin = "kotlin-jpa") | ||
|
||
apply(plugin = "org.springframework.boot") | ||
apply(plugin = "io.spring.dependency-management") | ||
|
@@ -55,6 +54,9 @@ subprojects { | |
implementation("com.fasterxml.jackson.module:jackson-module-kotlin") | ||
implementation("mysql:mysql-connector-java:8.0.32") | ||
|
||
// https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on | ||
implementation("org.bouncycastle:bcpkix-jdk15on:1.69") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오호라 이거는 꼭 추가해야하는 의존성인가요?! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저걸로 꼭 안해도 되긴하는데 없으면 힘드러요 흑흑 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 그럼 추가해야징~~~ |
||
|
||
testImplementation("org.springframework.boot:spring-boot-starter-test") | ||
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") | ||
testRuntimeOnly("org.junit.platform:junit-platform-launcher") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.wespot.error | ||
|
||
import feign.Response | ||
import feign.codec.ErrorDecoder | ||
import java.util.NoSuchElementException | ||
|
||
class CustomErrorDecoder : ErrorDecoder { | ||
override fun decode(methodKey: String, response: Response): Exception { | ||
return when (response.status()) { | ||
400 -> IllegalArgumentException("Bad request from OAuth") | ||
401 -> IllegalArgumentException("OAuth Authentication failed") | ||
404 -> NoSuchElementException("Resource not found from OAuth") | ||
500 -> RuntimeException("Internal server error from OAuth") | ||
else -> RuntimeException("Unknown error occurred from OAuth") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exception Message를 한글로 할 지, 영어로 할 지 현재는 통일성이 다소 떨어지는 것 같아요!(애플로그인 시 예외는 영어, 카카오 로그인 시 예외는 한글로 되어 있습니다.) 이 부분 함께 정해보면 좋을 것 같습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 한글로 하시죠! |
||
} | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 여기서 사용되는 ErrorDecoder는 OpenFeign에서만 사용되는것인가요?! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. import 확인해보니까 feign errordecoder 맞군요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네 맞아요! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.wespot.auth.dto | ||
|
||
import com.wespot.user.SocialType | ||
|
||
data class AuthLoginRequest( | ||
val socialType: SocialType, | ||
val authorizationCode: String?, | ||
val identityToken: String?, | ||
val fcmToken: String? | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.wespot.auth.dto | ||
|
||
data class OAuthIdAndRefreshToken( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AuthLoginRequest와 동일하게 AuthLoginResponse도 괜찮을 것 같아요! 사용목적이 조금 더 잘 드러나지 않나 싶습니다. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 해당 dto가 외부(컨트롤러)로 나가지 않은 dto여서 response를 안 붙였는데,, 붙이는게 좋을까요??👀 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아! 해당 DTO apple, kakao service가 반환하고 있어서 말씀드렸던 것인데, 컨트롤러로 나가지는 않는군요. 사실 And가 붙어서 그랬어요 흑흑 ㅠ |
||
val oAuthId: String, | ||
val refreshToken: String | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.wespot.auth.dto.apple | ||
|
||
/** | ||
* https://developer.apple.com/documentation/sign_in_with_apple/fetch_apple_s_public_key_for_verifying_token_signature 참고 | ||
*/ | ||
data class ApplePublicKeysResult( | ||
// 공개키 목록 | ||
val keys: List<ApplePublicKey> | ||
) { | ||
fun getMatchesKey(alg: String?, kid: String?): ApplePublicKey { | ||
return keys | ||
.firstOrNull { k -> k.alg == alg && k.kid == kid } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Kotlin은 String간의 비교도 == 연산으로 진행해도 되나보군요. 추가적으로 k가 아니라 key로 하시는 것은 어떤가요?! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네 수정하겠습니다! 이런 사소한 캐치 너무 좋아요! |
||
?: throw IllegalArgumentException("Apple JWT 값의 alg, kid 정보가 올바르지 않습니다.") | ||
} | ||
} | ||
|
||
data class ApplePublicKey( | ||
val kty: String, | ||
val kid: String, | ||
val use: String, | ||
val alg: String, | ||
val n: String, | ||
val e: String | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 물론 ApplePublicKeysResult에 포함되는 그저 data class이긴 하지만, 따로 class를 분리하지 않으신 이유가 있는 것일까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ApplePublicKey 가 ApplePublicKeysResult에서 사용되는 단순 데이터 구조였고, 긴밀하게 연관되어 있어서 같은 data class 끼리 묶었습니다 ㅎㅎ,, 분리해서 처리할게요! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.wespot.auth.dto.apple | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty | ||
|
||
/** | ||
* https://developer.apple.com/documentation/sign_in_with_apple/revoke_tokens | ||
*/ | ||
data class AppleRevokeRequest( | ||
@JsonProperty("client_id") | ||
val clientId: String, | ||
@JsonProperty("client_secret") | ||
val clientSecret: String, | ||
val token: String, | ||
@JsonProperty("token_type_hint") | ||
val tokenTypeHint: String | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package com.wespot.auth.dto.apple | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty | ||
|
||
/** | ||
* https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens 참고 | ||
*/ | ||
data class AppleTokenRequest( | ||
@JsonProperty("client_id") | ||
val clientId: String, | ||
@JsonProperty("client_secret") | ||
val clientSecret: String, | ||
val code: String?, | ||
@JsonProperty("grant_type") | ||
val grantType: String, | ||
@JsonProperty("redirect_uri") | ||
val redirectUri: String? | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.wespot.auth.dto.apple | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty | ||
|
||
/** | ||
* https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens 참고 | ||
*/ | ||
data class AppleTokenResult( | ||
@JsonProperty("access_token") | ||
val accessToken: String, | ||
@JsonProperty("token_type") | ||
val tokenType: String, | ||
@JsonProperty("expires_in") | ||
val expiresIn: Int, | ||
@JsonProperty("refresh_token") | ||
val refreshToken: String?, | ||
@JsonProperty("id_token") | ||
val idToken: String, | ||
val error: String?, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.wespot.auth.dto.kakao | ||
|
||
data class KakaoRevokeResult( | ||
val id : Long, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.wespot.auth.dto.kakao | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty | ||
|
||
/** | ||
* https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#request-token-request-body 참고 | ||
*/ | ||
data class KakaoTokenRequest( | ||
@JsonProperty("grant_type") | ||
val grantType: String, | ||
@JsonProperty("client_id") | ||
val clientId: String, | ||
@JsonProperty("redirect_uri") | ||
val redirectUri: String, | ||
val code: String, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.wespot.auth.dto.kakao | ||
|
||
import com.fasterxml.jackson.annotation.JsonProperty | ||
|
||
/** | ||
* https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#request-token-response 참고 | ||
*/ | ||
data class KakaoTokenResult( | ||
@JsonProperty("access_token") | ||
val accessToken: String, | ||
@JsonProperty("token_type") | ||
val tokenType: String, | ||
@JsonProperty("refresh_token") | ||
val refreshToken: String?, | ||
@JsonProperty("refresh_token_expires_in") | ||
val refreshTokenExpiresIn: String?, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.wespot.auth.dto.kakao | ||
|
||
/** | ||
* https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api#req-user-info-response 참고 | ||
*/ | ||
data class KakaoUserInfoResult( | ||
val id: Long, | ||
val connected_at: String, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.wespot.auth.service | ||
|
||
import com.wespot.auth.dto.AuthLoginRequest | ||
import com.wespot.auth.dto.OAuthIdAndRefreshToken | ||
import com.wespot.user.SocialType | ||
|
||
interface SocialAuthService { | ||
fun fetchAuthToken(authLoginRequest: AuthLoginRequest): OAuthIdAndRefreshToken | ||
fun isSupport(socialType: SocialType): Boolean | ||
fun revoke(socialId: String, socialRefreshToken: String?): Boolean | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.wespot.auth.service | ||
|
||
import com.wespot.user.SocialType | ||
import org.springframework.stereotype.Component | ||
|
||
@Component | ||
class SocialAuthServiceFactory( | ||
private val services: List<SocialAuthService> | ||
) { | ||
fun getService(socialType: SocialType): SocialAuthService { | ||
return services.find { it.isSupport(socialType) } | ||
?: throw IllegalArgumentException("Unsupported social type: $socialType") | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아주 멋있는 구조입니다~~ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.wespot.auth.service | ||
|
||
import feign.Logger | ||
import feign.RequestInterceptor | ||
import feign.RequestTemplate | ||
import org.springframework.context.annotation.Bean | ||
import org.springframework.http.MediaType | ||
|
||
class SocialHeaderConfiguration { | ||
companion object { | ||
private const val CONTENT_TYPE_HEADER = "Content-Type" | ||
} | ||
|
||
@Bean | ||
fun requestInterceptor(): RequestInterceptor { | ||
return RequestInterceptor { template: RequestTemplate -> | ||
template.header( | ||
CONTENT_TYPE_HEADER, | ||
MediaType.APPLICATION_FORM_URLENCODED_VALUE | ||
) | ||
} | ||
} | ||
|
||
@Bean | ||
fun feignLoggerLevel(): Logger.Level { | ||
return Logger.Level.FULL | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package com.wespot.auth.service.apple | ||
|
||
import com.wespot.auth.dto.apple.ApplePublicKeysResult | ||
import com.wespot.auth.dto.apple.AppleRevokeRequest | ||
import com.wespot.auth.dto.apple.AppleTokenResult | ||
import com.wespot.auth.service.SocialHeaderConfiguration | ||
import feign.Response | ||
import org.springframework.cloud.openfeign.FeignClient | ||
import org.springframework.http.MediaType | ||
import org.springframework.web.bind.annotation.GetMapping | ||
import org.springframework.web.bind.annotation.PostMapping | ||
import org.springframework.web.bind.annotation.RequestBody | ||
import org.springframework.web.bind.annotation.RequestParam | ||
|
||
@FeignClient(name = "apple", url = "https://appleid.apple.com/auth", configuration = [SocialHeaderConfiguration::class]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @FeignClient(
name = "apple",
url = "https://appleid.apple.com/auth",
configuration = [SocialHeaderConfiguration::class]
) 이렇게 긴 경우에는, 개행을 해주는 것도 좋을 것 같아용 |
||
interface AppleClient { | ||
|
||
/** | ||
* Apple 공개키 가져오기 | ||
* https://appleid.apple.com/auth/keys | ||
*/ | ||
@GetMapping("/keys") | ||
fun getApplePublicKeys(): ApplePublicKeysResult | ||
|
||
/** | ||
* Apple get Token | ||
* https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens | ||
*/ | ||
@PostMapping("/token", produces = [MediaType.APPLICATION_FORM_URLENCODED_VALUE]) | ||
fun getToken( | ||
@RequestParam("client_id") clientId: String, | ||
@RequestParam("client_secret") clientSecret: String, | ||
@RequestParam("code") code: String, | ||
@RequestParam("grant_type") grantType: String, | ||
@RequestParam("redirect_uri") redirectUri: String, | ||
): AppleTokenResult | ||
|
||
/** | ||
* Apple revoke Token | ||
* https://developer.apple.com/documentation/sign_in_with_apple/revoking_tokens | ||
*/ | ||
@PostMapping("/revoke", produces = [MediaType.APPLICATION_FORM_URLENCODED_VALUE]) | ||
fun revoke( | ||
@RequestBody appleRevokeRequest: AppleRevokeRequest | ||
): Response | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EOF 추가하면 좋을 것 같아요!!