Skip to content

Commit

Permalink
refac: 코루틴 사용방식 변경
Browse files Browse the repository at this point in the history
  • Loading branch information
wjdtkdgns committed Nov 9, 2024
1 parent 3de1dfa commit c4c114d
Show file tree
Hide file tree
Showing 34 changed files with 269 additions and 340 deletions.
49 changes: 18 additions & 31 deletions src/main/kotlin/com/balancemania/api/auth/application/AuthFacade.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.balancemania.api.auth.application

import arrow.fx.coroutines.parZip
import com.balancemania.api.auth.application.domain.RefreshToken
import com.balancemania.api.auth.model.AuthUser
import com.balancemania.api.auth.model.AuthUserImpl
Expand All @@ -10,13 +9,11 @@ import com.balancemania.api.auth.model.response.TokenRefreshRequest
import com.balancemania.api.config.database.TransactionTemplates
import com.balancemania.api.exception.ErrorCode
import com.balancemania.api.exception.NoAuthorityException
import com.balancemania.api.extension.coExecuteOrNull
import com.balancemania.api.extension.executeNotNull
import com.balancemania.api.user.application.UserService
import com.balancemania.api.user.domain.vo.UserStatusType
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import org.springframework.context.ApplicationEventPublisher
import kotlinx.coroutines.*
import kotlinx.coroutines.slf4j.MDCContext
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

Expand All @@ -26,7 +23,6 @@ class AuthFacade(
private val jwtTokenService: JwtTokenService,
private val refreshTokenService: RefreshTokenService,
private val oAuthService: OAuthService,
private val eventPublisher: ApplicationEventPublisher,
private val txTemplates: TransactionTemplates,
) {
fun resolveAuthUser(token: AuthUserToken): Any {
Expand Down Expand Up @@ -54,51 +50,42 @@ class AuthFacade(
}

@Transactional
suspend fun logout(user: AuthUser) {
fun logout(user: AuthUser) {
refreshTokenService.deleteByKey(user.uid.toString())
}

@Transactional
suspend fun refreshToken(request: TokenRefreshRequest): TokenDto {
fun refreshToken(request: TokenRefreshRequest): TokenDto {
val accessPayload = jwtTokenService.verifyTokenWithExtendedExpiredAt(request.accessToken)
val refreshPayload = jwtTokenService.verifyRefreshToken(request.refreshToken)

if (accessPayload.id != refreshPayload.id) {
throw NoAuthorityException(ErrorCode.INVALID_TOKEN)
}

return parZip(
{ refreshTokenService.deleteByKey(refreshPayload.id.toString()) },
{ jwtTokenService.generateAccessAndRefreshToken(refreshPayload.id) }
) { _, tokenDto ->
CoroutineScope(Dispatchers.IO + MDCContext()).launch {
refreshTokenService.deleteByKey(refreshPayload.id.toString())
}

return jwtTokenService.generateAccessAndRefreshToken(refreshPayload.id).also {
RefreshToken(
uid = refreshPayload.id,
refreshToken = tokenDto.refreshToken
refreshToken = it.refreshToken
).run { refreshTokenService.save(this) }

tokenDto
}
}

@Transactional
suspend fun withdraw(authUser: AuthUser) {
fun withdraw(authUser: AuthUser) {
val user = userService.findByIdOrThrow(authUser.uid)

coroutineScope {
val txDeferred = async {
txTemplates.writer.coExecuteOrNull {
user.apply {
this.oauthInfo = oauthInfo.withdrawOAuthInfo()
this.statusType = UserStatusType.DELETED
}.run { userService.saveSync(this) }
}
}

val oAuthDeferred = async {
oAuthService.withdraw(user.oauthInfo)
}
oAuthService.withdraw(user.oauthInfo)

awaitAll(txDeferred, oAuthDeferred)
txTemplates.writer.executeNotNull() {
user.apply {
this.oauthInfo = oauthInfo.withdrawOAuthInfo()
this.statusType = UserStatusType.DELETED
}.run { userService.saveSync(this) }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ class DevOAuthService(
private val logger = KotlinLogging.logger { }

/** oauth login link 가져오기 */
suspend fun getOAuthLoginLinkDev(provider: OAuthProvider): OAuthLoginLinkResponse {
fun getOAuthLoginLinkDev(provider: OAuthProvider): OAuthLoginLinkResponse {
return when (provider) {
OAuthProvider.KAKAO -> kakaoOAuthService.getOAuthLoginLinkDev()
}
}

/** oauth token 가져오기 */
suspend fun getOAuthTokenDev(provider: OAuthProvider, code: String): OAuthTokenResponse {
fun getOAuthTokenDev(provider: OAuthProvider, code: String): OAuthTokenResponse {
return when (provider) {
OAuthProvider.KAKAO -> kakaoOAuthService.getOAuthTokenDev(code)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import com.balancemania.api.extension.toInstant
import com.fasterxml.jackson.module.kotlin.readValue
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.stereotype.Service
import reactor.core.publisher.Mono
import java.time.LocalDateTime
import java.util.Date

Expand Down Expand Up @@ -74,16 +73,6 @@ class JwtTokenService(
return mapper.readValue(payload)
}

fun verifyTokenMono(authUserToken: Mono<AuthUserToken>): Mono<AuthUserTokenPayload> {
return authUserToken.flatMap { jwtToken ->
Mono.fromCallable { verifyToken(jwtToken) }
.onErrorResume { e ->
logger.warn { e.message }
Mono.error(InvalidTokenException(ErrorCode.FAIL_TO_VERIFY_TOKEN_ERROR))
}
}
}

fun createRefreshToken(id: Long, refreshTokenExpiresAt: LocalDateTime): String {
return JWT.create().apply {
this.withIssuer(jwtConfig.issuer)
Expand All @@ -94,7 +83,7 @@ class JwtTokenService(
}.sign(Algorithm.HMAC256(jwtConfig.secretKey))
}

suspend fun verifyRefreshToken(refreshToken: String): AuthUserTokenPayload {
fun verifyRefreshToken(refreshToken: String): AuthUserTokenPayload {
val payload = runCatching { refreshJwtVerifier.verify(refreshToken).payload.decodeBase64() }
.getOrNull() ?: throw InvalidTokenException(ErrorCode.INVALID_REFRESH_TOKEN)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ import com.balancemania.api.auth.model.request.OAuthRegisterRequest
import com.balancemania.api.auth.model.response.AbleRegisterResponse
import com.balancemania.api.auth.model.response.UserOAuthInfoResponse
import com.balancemania.api.config.database.TransactionTemplates
import com.balancemania.api.extension.coExecute
import com.balancemania.api.extension.executeNotNull
import com.balancemania.api.user.application.UserService
import com.balancemania.api.user.domain.User
import com.balancemania.api.user.domain.vo.OAuthProvider
import com.balancemania.api.user.domain.vo.UserStatusType
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.Dispatchers
import org.springframework.stereotype.Service

@Service
Expand All @@ -28,7 +27,7 @@ class OAuthFacade(
val logger = KotlinLogging.logger {}

/** 회원가입 가능 여부 체크. */
suspend fun checkRegisterValid(provider: OAuthProvider, accessToken: String): AbleRegisterResponse {
fun checkRegisterValid(provider: OAuthProvider, accessToken: String): AbleRegisterResponse {
val oauthInfo = oAuthService.getOAuthInfo(provider, accessToken)

val isExistUser = userService.existsByOAuthInfo(oauthInfo)
Expand All @@ -37,7 +36,7 @@ class OAuthFacade(
}

/** 회원가입 */
suspend fun register(
fun register(
provider: OAuthProvider,
accessToken: String,
request: OAuthRegisterRequest,
Expand All @@ -46,23 +45,21 @@ class OAuthFacade(

userService.validateNotRegistered(oauthInfo)

val user = txTemplates.writer.coExecute(Dispatchers.IO) {
val createdUser = User(
val user = txTemplates.writer.executeNotNull {
User(
oauthInfo = oauthInfo,
name = request.name,
gender = request.gender,
birth = request.getBirth(),
statusType = UserStatusType.ACTIVE
).run { userService.saveSync(this) }

createdUser
}

return generateTokenDto(user.id)
}

/** 로그인 */
suspend fun login(
fun login(
provider: OAuthProvider,
request: OAuthLoginRequest,
): TokenDto {
Expand All @@ -72,16 +69,14 @@ class OAuthFacade(
return generateTokenDto(user.id)
}

private suspend fun generateTokenDto(uid: Long): TokenDto {
val tokenDto = jwtTokenService.generateAccessAndRefreshToken(uid)

RefreshToken(uid = uid, refreshToken = tokenDto.refreshToken)
.run { refreshTokenService.save(this) }

return tokenDto
private fun generateTokenDto(uid: Long): TokenDto {
return jwtTokenService.generateAccessAndRefreshToken(uid).also {
RefreshToken(uid = uid, refreshToken = it.refreshToken)
.run { refreshTokenService.save(this) }
}
}

suspend fun getOAuthInfo(user: AuthUser): UserOAuthInfoResponse {
fun getOAuthInfo(user: AuthUser): UserOAuthInfoResponse {
return userService.findByIdOrThrow(user.uid).run { UserOAuthInfoResponse.from(this) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ class OAuthService(
private val logger = KotlinLogging.logger { }

/** oauth withdraw 페이지용 login link 가져오기 */
suspend fun getOAuthWithdrawLoginLink(provider: OAuthProvider, uri: String): OAuthLoginLinkResponse {
fun getOAuthWithdrawLoginLink(provider: OAuthProvider, uri: String): OAuthLoginLinkResponse {
return when (provider) {
OAuthProvider.KAKAO -> kakaoOAuthService.getOAuthWithdrawLoginLink(uri)
}
}

/** oauth token 가져오기 */
suspend fun getOAuthWithdrawToken(
fun getOAuthWithdrawToken(
provider: OAuthProvider,
code: String,
): OAuthTokenResponse {
Expand All @@ -32,7 +32,7 @@ class OAuthService(
}

/** oauth info 가져오기 */
suspend fun getOAuthInfo(
fun getOAuthInfo(
provider: OAuthProvider,
accessToken: String,
): OauthInfo {
Expand All @@ -42,7 +42,7 @@ class OAuthService(
}

/** oauth 유저 회원 탈퇴하기 */
suspend fun withdraw(oauthInfo: OauthInfo) {
fun withdraw(oauthInfo: OauthInfo) {
when (oauthInfo.oAuthProvider) {
OAuthProvider.KAKAO -> kakaoOAuthService.withdraw(oauthInfo.oAuthId)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,18 @@ package com.balancemania.api.auth.application
import com.balancemania.api.auth.application.domain.RefreshToken
import com.balancemania.api.auth.application.infrastructure.RefreshTokenRepository
import com.balancemania.api.config.auth.JwtConfig
import com.balancemania.api.extension.withMDCContext
import kotlinx.coroutines.Dispatchers
import org.springframework.stereotype.Service

@Service
class RefreshTokenService(
private val refreshTokenRepository: RefreshTokenRepository,
private val jwtConfig: JwtConfig,
) {
suspend fun deleteByKey(key: String) {
fun deleteByKey(key: String) {
refreshTokenRepository.deleteByKey(key)
}

suspend fun save(token: RefreshToken) {
withMDCContext(Dispatchers.IO) {
refreshTokenRepository.save(token, jwtConfig.refreshExp.toLong())
}
fun save(token: RefreshToken) {
refreshTokenRepository.save(token, jwtConfig.refreshExp.toLong())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import org.springframework.stereotype.Repository
class RefreshTokenRepository(
private val cacheService: CacheService,
) {
suspend fun save(value: RefreshToken, ttl: Long) {
fun save(value: RefreshToken, ttl: Long) {
cacheService.set(
cache = getCache(value.uid.toString(), ttl),
value = value.refreshToken
)
}

suspend fun deleteByKey(key: String) {
fun deleteByKey(key: String) {
cacheService.delete(cache = getCache(key, 0))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import com.balancemania.api.auth.model.response.OAuthTokenResponse
import com.balancemania.api.client.oauth.kakao.KakaoClient
import com.balancemania.api.config.auth.OAuthSecretConfig
import com.balancemania.api.config.auth.OAuthUrlConfig
import com.balancemania.api.extension.withMDCContext
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.Dispatchers
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service

Expand All @@ -23,7 +21,7 @@ class KakaoOAuthService(
private val logger = KotlinLogging.logger { }

/** link */
suspend fun getOAuthLoginLinkDev(): OAuthLoginLinkResponse {
fun getOAuthLoginLinkDev(): OAuthLoginLinkResponse {
val redirectUrl = domainName + kakaoOAuthUrlConfig.redirectUrl
return OAuthLoginLinkResponse(
kakaoOAuthUrlConfig.kauthUrl +
Expand All @@ -35,7 +33,7 @@ class KakaoOAuthService(
)
}

suspend fun getOAuthWithdrawLoginLink(uri: String): OAuthLoginLinkResponse {
fun getOAuthWithdrawLoginLink(uri: String): OAuthLoginLinkResponse {
val redirectUrl = domainName + kakaoOAuthUrlConfig.withdrawCallbackUrl
return OAuthLoginLinkResponse(
kakaoOAuthUrlConfig.kauthUrl +
Expand All @@ -48,38 +46,33 @@ class KakaoOAuthService(
}

/** oauth token 받아오기 */
suspend fun getOAuthTokenDev(code: String): OAuthTokenResponse {
fun getOAuthTokenDev(code: String): OAuthTokenResponse {
val redirectUrl = domainName + kakaoOAuthUrlConfig.redirectUrl
return getKakaoToken(redirectUrl, code)
}

suspend fun getOAuthWithdrawToken(code: String): OAuthTokenResponse {
fun getOAuthWithdrawToken(code: String): OAuthTokenResponse {
val redirectUrl = domainName + kakaoOAuthUrlConfig.withdrawCallbackUrl
return getKakaoToken(redirectUrl, code)
}

private suspend fun getKakaoToken(redirectUrl: String, code: String): OAuthTokenResponse {
return withMDCContext(Dispatchers.IO) {
kakaoClient.getToken(
redirectUrl = redirectUrl,
code = code,
clientId = kakaoOAuthSecretConfig.clientId,
clientSecret = kakaoOAuthSecretConfig.clientSecret
)
}.run { OAuthTokenResponse.fromKakao(this) }
private fun getKakaoToken(redirectUrl: String, code: String): OAuthTokenResponse {
return kakaoClient.getToken(
redirectUrl = redirectUrl,
code = code,
clientId = kakaoOAuthSecretConfig.clientId,
clientSecret = kakaoOAuthSecretConfig.clientSecret
).run { OAuthTokenResponse.fromKakao(this) }
}

/** 유저 정보를 가져옵니다. */
suspend fun getOAuthInfo(accessToken: String): OAuthUserInfoDto {
return withMDCContext(Dispatchers.IO) {
kakaoClient.getUserInfo(accessToken)
}.run { OAuthUserInfoDto.fromKakao(this) }
fun getOAuthInfo(accessToken: String): OAuthUserInfoDto {
return kakaoClient.getUserInfo(accessToken)
.run { OAuthUserInfoDto.fromKakao(this) }
}

/** 회원 탈퇴합니다 */
suspend fun withdraw(oAuthId: String) {
withMDCContext(Dispatchers.IO) {
kakaoClient.withdraw(targetId = oAuthId, adminKey = kakaoOAuthSecretConfig.adminKey)
}
fun withdraw(oAuthId: String) {
kakaoClient.withdraw(targetId = oAuthId, adminKey = kakaoOAuthSecretConfig.adminKey)
}
}
Loading

0 comments on commit c4c114d

Please sign in to comment.