Skip to content

Commit

Permalink
fix: OAuth 로그인 및 회원가입 여부
Browse files Browse the repository at this point in the history
  • Loading branch information
DongGeon0908 committed Jul 30, 2024
1 parent bdf7029 commit bd4f514
Show file tree
Hide file tree
Showing 24 changed files with 238 additions and 42 deletions.
17 changes: 14 additions & 3 deletions sql/DDL.sql
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ CREATE TABLE `user_info`
`modified_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=200000 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='유저 정보';

CREATE UNIQUE INDEX uidx__nickname ON user_info (nickname);

-- 일반 회원가입 유저 정보
Expand All @@ -25,10 +24,23 @@ CREATE TABLE `credential_user_info`
`modified_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=200000 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='유저 정보';

CREATE UNIQUE INDEX uidx__uid ON credential_user_info (uid);
CREATE UNIQUE INDEX uidx__username ON credential_user_info (username);

-- OAuth 회원가입 유저 정보
CREATE TABLE `oauth_user_info`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'oauth user info id',
`uid` bigint NOT NULL COMMENT 'user id',
`provider` varchar(32) NOT NULL COMMENT 'oauth provider',
`oauth_id` varchar(512) NOT NULL COMMENT 'oauth id',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
`modified_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=200000 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='oauth 유저 정보';
CREATE UNIQUE INDEX uidx__uid__provider ON oauth_user_info (uid, provider);
CREATE INDEX idx__oauth_id__provider ON oauth_user_info (oauth_id, provider);

-- 그룹
CREATE TABLE `group`
(
Expand Down Expand Up @@ -57,7 +69,6 @@ CREATE TABLE `group_user`
`modified_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=200000 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='그룹 유저';

CREATE UNIQUE INDEX uidx__group_id__uid ON group_user (group_id, uid);
CREATE INDEX idx__uid ON group_user (uid);

Expand Down
23 changes: 20 additions & 3 deletions src/main/kotlin/com/hero/alignlab/client/kakao/KakaoInfoService.kt
Original file line number Diff line number Diff line change
@@ -1,20 +1,37 @@
package com.hero.alignlab.client.kakao

import com.hero.alignlab.client.kakao.client.KaKaoInfoClient
import com.hero.alignlab.client.kakao.client.KakaoInfoClient
import com.hero.alignlab.client.kakao.model.response.KakaoOAuthUserInfoResponse
import com.hero.alignlab.common.extension.resolveCancellation
import com.hero.alignlab.domain.auth.model.OAuthProvider
import com.hero.alignlab.domain.auth.model.OAuthUserInfoModel
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.springframework.stereotype.Service

@Service
class KakaoInfoService(
private val kaKaoInfoClient: KaKaoInfoClient,
private val kaKaoInfoClient: KakaoInfoClient,
) {
private val logger = KotlinLogging.logger { }

suspend fun getOAuthInfo(accessToken: String): OAuthUserInfoModel {
val oauthInfo = getUserInfo(accessToken)

return OAuthUserInfoModel(
provider = OAuthProvider.kakao,
oauthId = oauthInfo.id,
nickname = oauthInfo.kakaoAccount.profile.nickname,
profileImageUrl = oauthInfo.kakaoAccount.profile.profileImageUrl
)
}

suspend fun getUserInfo(accessToken: String): KakaoOAuthUserInfoResponse {
return runCatching {
kaKaoInfoClient.getUserInfo(accessToken)
withContext(Dispatchers.IO) {
kaKaoInfoClient.getUserInfo(accessToken)
}
}.onFailure { e ->
logger.resolveCancellation("getUserInfo", e)
}.getOrThrow()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.hero.alignlab.client.kakao

import com.hero.alignlab.client.kakao.client.KaKaoOAuthClient
import com.hero.alignlab.client.kakao.client.KakaoOAuthClient
import com.hero.alignlab.client.kakao.config.KakaoOAuthClientConfig
import com.hero.alignlab.client.kakao.model.request.GenerateKakaoOAuthTokenRequest
import com.hero.alignlab.client.kakao.model.response.GenerateKakaoOAuthTokenResponse
Expand All @@ -12,7 +12,7 @@ import org.springframework.stereotype.Service

@Service
class KakaoOAuthService(
private val kaKaoOAuthClient: KaKaoOAuthClient,
private val kakaoOAuthClient: KakaoOAuthClient,
private val config: KakaoOAuthClientConfig.Config
) {
private val logger = KotlinLogging.logger {}
Expand All @@ -28,7 +28,7 @@ class KakaoOAuthService(
suspend fun getOAuthAuthorizeCode(clientId: String, redirectUri: String) {
runCatching {
withContext(Dispatchers.IO) {
kaKaoOAuthClient.getOAuthAuthorizeCode(clientId, redirectUri)
kakaoOAuthClient.getOAuthAuthorizeCode(clientId, redirectUri)
}
}.onFailure { e ->
logger.resolveCancellation("getOAuthAuthorizeCode", e)
Expand All @@ -49,7 +49,7 @@ class KakaoOAuthService(
suspend fun generateOAuthToken(request: GenerateKakaoOAuthTokenRequest): GenerateKakaoOAuthTokenResponse {
return runCatching {
withContext(Dispatchers.IO) {
kaKaoOAuthClient.generateOAuthToken(request)
kakaoOAuthClient.generateOAuthToken(request)
}
}.onFailure { e ->
logger.resolveCancellation("generateOAuthToken", e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ package com.hero.alignlab.client.kakao.client

import com.hero.alignlab.client.kakao.model.response.KakaoOAuthUserInfoResponse

interface KaKaoInfoClient {
interface KakaoInfoClient {
suspend fun getUserInfo(accessToken: String): KakaoOAuthUserInfoResponse
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.hero.alignlab.client.kakao.client
import com.hero.alignlab.client.kakao.model.request.GenerateKakaoOAuthTokenRequest
import com.hero.alignlab.client.kakao.model.response.GenerateKakaoOAuthTokenResponse

interface KaKaoOAuthClient {
interface KakaoOAuthClient {
suspend fun getOAuthAuthorizeCode(clientId: String, redirectUri: String)

suspend fun generateOAuthToken(request: GenerateKakaoOAuthTokenRequest): GenerateKakaoOAuthTokenResponse
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.hero.alignlab.client.kakao.SuspendableClient
import com.hero.alignlab.client.kakao.model.response.KakaoOAuthUserInfoResponse
import org.springframework.web.reactive.function.client.WebClient

class SuspendableKakaoInfoClient(client: WebClient) : KaKaoInfoClient, SuspendableClient(client) {
class SuspendableKakaoInfoClient(client: WebClient) : KakaoInfoClient, SuspendableClient(client) {
override suspend fun getUserInfo(accessToken: String): KakaoOAuthUserInfoResponse {
return client.get()
.uri("/v2/user/me")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.hero.alignlab.client.kakao.model.request.GenerateKakaoOAuthTokenReque
import com.hero.alignlab.client.kakao.model.response.GenerateKakaoOAuthTokenResponse
import org.springframework.web.reactive.function.client.WebClient

class SuspendableKakaoOAuthClient(client: WebClient) : KaKaoOAuthClient, SuspendableClient(client) {
class SuspendableKakaoOAuthClient(client: WebClient) : KakaoOAuthClient, SuspendableClient(client) {
override suspend fun getOAuthAuthorizeCode(clientId: String, redirectUri: String) {
client
.get()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.hero.alignlab.client.kakao.config

import com.hero.alignlab.client.WebClientFactory
import com.hero.alignlab.client.kakao.client.KaKaoInfoClient
import com.hero.alignlab.client.kakao.client.KaKaoOAuthClient
import com.hero.alignlab.client.kakao.client.KakaoInfoClient
import com.hero.alignlab.client.kakao.client.KakaoOAuthClient
import com.hero.alignlab.client.kakao.client.SuspendableKakaoInfoClient
import io.github.oshai.kotlinlogging.KotlinLogging
import jakarta.validation.Valid
Expand All @@ -27,10 +27,10 @@ class KakaoInfoClientConfig {

@Bean
@ConditionalOnBean(name = ["kakaoInfoConfig"])
@ConditionalOnMissingBean(KaKaoOAuthClient::class)
@ConditionalOnMissingBean(KakaoOAuthClient::class)
fun kakaoInfoClient(
@Valid kakaoInfoConfig: Config
): KaKaoInfoClient {
): KakaoInfoClient {
logger.info { "initialized KaKaoInfoClient. $kakaoInfoConfig" }

val webclient = WebClientFactory.generate(kakaoInfoConfig.url)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.hero.alignlab.client.kakao.config

import com.hero.alignlab.client.WebClientFactory
import com.hero.alignlab.client.kakao.client.KaKaoOAuthClient
import com.hero.alignlab.client.kakao.client.KakaoOAuthClient
import com.hero.alignlab.client.kakao.client.SuspendableKakaoOAuthClient
import io.github.oshai.kotlinlogging.KotlinLogging
import jakarta.validation.Valid
Expand All @@ -26,10 +26,10 @@ class KakaoOAuthClientConfig {

@Bean
@ConditionalOnBean(name = ["kakaoOAuthConfig"])
@ConditionalOnMissingBean(KaKaoOAuthClient::class)
@ConditionalOnMissingBean(KakaoOAuthClient::class)
fun kakaoOAuthClient(
@Valid kakaoOAuthConfig: Config
): KaKaoOAuthClient {
): KakaoOAuthClient {
logger.info { "initialized kakaoOAuthClient. $kakaoOAuthConfig" }

val webclient = WebClientFactory.generate(kakaoOAuthConfig.url)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ data class KakaoOAuthUserInfoResponse(
val hasSignedUp: Boolean?,
val connectedAt: LocalDateTime?,
val synchedAt: LocalDateTime?,
val kakaoAccount: KakaoAccount?,
val kakaoAccount: KakaoAccount,
) {
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class KakaoAccount(
val profileNeedsAgreement: Boolean?,
val profile: Profile?,
val profile: Profile,
val nameNeedsAgreement: Boolean?,
val name: String?,
val emailNeedsAgreement: Boolean?,
Expand All @@ -40,7 +40,7 @@ data class KakaoOAuthUserInfoResponse(

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class Profile(
val nickname: String?,
val nickname: String,
val thumbnailImageUrl: String?,
val profileImageUrl: String?,
val isDefaultImage: Boolean?,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,47 @@
package com.hero.alignlab.domain.auth.application

import com.hero.alignlab.client.kakao.KakaoOAuthService
import com.hero.alignlab.common.extension.toJson
import com.hero.alignlab.domain.auth.model.OAuthProvider
import com.hero.alignlab.domain.auth.model.request.OAuthAuthorizedRequest
import com.hero.alignlab.domain.auth.model.response.OAuthAuthorizeCodeResponse
import com.hero.alignlab.domain.auth.model.request.OAuthLoginRequest
import com.hero.alignlab.domain.auth.model.response.OAuthCheckSignUpResponse
import com.hero.alignlab.domain.auth.model.response.OAuthLoginResponse
import com.hero.alignlab.domain.user.application.OAuthUserInfoService
import com.hero.alignlab.domain.user.application.UserInfoService
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.stereotype.Service
import java.time.LocalDateTime

@Service
class OAuthFacade(
private val kakaoOAuthService: KakaoOAuthService,
private val oAuthService: OAuthService,
private val oAuthUserInfoService: OAuthUserInfoService,
private val userInfoService: UserInfoService,
private val jwtTokenService: JwtTokenService,
) {
companion object {
private val TOKEN_EXPIRED_DATE = LocalDateTime.of(2024, 12, 29, 0, 0, 0)
}

private val logger = KotlinLogging.logger { }

suspend fun getOAuthAuthorizeCode(provider: OAuthProvider): OAuthAuthorizeCodeResponse {
when (provider) {
OAuthProvider.kakao -> kakaoOAuthService.getOAuthAuthorizeCode()
}
suspend fun checkSignUp(provider: OAuthProvider, accessToken: String): OAuthCheckSignUpResponse {
val oauthInfo = oAuthService.getOAuthInfo(provider, accessToken)

val isExists = oAuthUserInfoService.existsByOauthIdAndProvider(oauthInfo.oauthId, provider.toProvider())

return OAuthAuthorizeCodeResponse(provider)
return OAuthCheckSignUpResponse(isExists)
}

suspend fun resolveOAuth(provider: OAuthProvider, request: OAuthAuthorizedRequest) {
val response = when (provider) {
OAuthProvider.kakao -> kakaoOAuthService.generateOAuthToken(request.code)
}
suspend fun signIn(provider: OAuthProvider, request: OAuthLoginRequest): OAuthLoginResponse? {
val oauthInfo = oAuthService.getOAuthInfo(provider, request.accessToken)

val userInfo = userInfoService.findByOAuthOrThrow(provider.toProvider(), oauthInfo.oauthId)

val token = jwtTokenService.createToken(userInfo.id, TOKEN_EXPIRED_DATE)

println("response > " + response.toJson())
return OAuthLoginResponse(
uid = userInfo.id,
nickname = userInfo.nickname,
accessToken = token
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.hero.alignlab.domain.auth.application

import com.hero.alignlab.client.kakao.KakaoInfoService
import com.hero.alignlab.domain.auth.model.OAuthProvider
import com.hero.alignlab.domain.auth.model.OAuthUserInfoModel
import org.springframework.stereotype.Service

@Service
class OAuthService(
private val kakaoInfoService: KakaoInfoService,
) {
suspend fun getOAuthInfo(provider: OAuthProvider, accessToken: String): OAuthUserInfoModel {
return when (provider) {
OAuthProvider.kakao -> kakaoInfoService.getOAuthInfo(accessToken)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package com.hero.alignlab.domain.auth.model

import com.hero.alignlab.domain.user.domain.OAuthProvider

enum class OAuthProvider {
kakao,
;

fun toProvider(): OAuthProvider {
return when (this) {
kakao -> OAuthProvider.KAKAO
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.hero.alignlab.domain.auth.model

data class OAuthUserInfoModel(
val provider: OAuthProvider,
val oauthId: String,
val nickname: String,
val profileImageUrl: String?
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.hero.alignlab.domain.auth.model.request

data class OAuthLoginRequest(
val accessToken: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.hero.alignlab.domain.auth.model.response

data class OAuthCheckSignUpResponse(
val isExistsUser: Boolean,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.hero.alignlab.domain.auth.model.response

data class OAuthLoginResponse(
val uid: Long,
val nickname: String,
/** hero access token */
val accessToken: String,
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.hero.alignlab.domain.auth.resource

import com.hero.alignlab.common.extension.wrapOk
import com.hero.alignlab.domain.auth.application.OAuthFacade
import com.hero.alignlab.domain.auth.model.OAuthProvider
import com.hero.alignlab.domain.auth.model.request.OAuthLoginRequest
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.*
Expand All @@ -14,5 +18,17 @@ import org.springframework.web.bind.annotation.*
class OAuthResource(
private val oAuthFacade: OAuthFacade,
) {
@Operation(summary = "회원가입 여부 확인")
@GetMapping("/api/v1/oauth/{provider}/sign-up/check")
suspend fun checkSignUp(
@PathVariable provider: OAuthProvider,
@RequestParam accessToken: String,
) = oAuthFacade.checkSignUp(provider, accessToken).wrapOk()

@Operation(summary = "로그인")
@PostMapping("/api/v1/oauth/{provider}/sign-in")
suspend fun signIn(
@PathVariable provider: OAuthProvider,
@RequestBody request: OAuthLoginRequest,
) = oAuthFacade.signIn(provider, request).wrapOk()
}
Loading

0 comments on commit bd4f514

Please sign in to comment.