Skip to content

Commit

Permalink
feat: add save RefreshToken logic (#62)
Browse files Browse the repository at this point in the history
* feat: add save RefreshToken logic

* chore: add redis

* style: ktlint
  • Loading branch information
jhhong0509 authored Jun 24, 2022
1 parent e717838 commit 1f5c79f
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 3 deletions.
3 changes: 3 additions & 0 deletions buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,7 @@ object Dependencies {

// cloud
const val SPRING_CLOUD = "org.springframework.cloud:spring-cloud-dependencies:${DependencyVersions.SPRING_CLOUD_VERSION}"

// redis
const val REACTIVE_REDIS = "org.springframework.boot:spring-boot-starter-data-redis-reactive"
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import com.xquare.v1userservice.user.api.dtos.SignInDomainRequest
import com.xquare.v1userservice.user.api.dtos.SignInResponse
import com.xquare.v1userservice.user.exceptions.PasswordNotMatchesException
import com.xquare.v1userservice.user.exceptions.UserNotFoundException
import com.xquare.v1userservice.user.refreshtoken.RefreshToken
import com.xquare.v1userservice.user.refreshtoken.spi.RefreshTokenSpi
import com.xquare.v1userservice.user.spi.AuthorityListSpi
import com.xquare.v1userservice.user.spi.JwtTokenGeneratorSpi
import com.xquare.v1userservice.user.spi.PasswordMatcherSpi
Expand All @@ -19,7 +21,8 @@ class UserSignInApiImpl(
private val userRepositorySpi: UserRepositorySpi,
private val passwordMatcherSpi: PasswordMatcherSpi,
private val jwtTokenGeneratorSpi: JwtTokenGeneratorSpi,
private val authorityListSpi: AuthorityListSpi
private val authorityListSpi: AuthorityListSpi,
private val refreshTokenSpi: RefreshTokenSpi
) : UserSignInApi {
override suspend fun userSignIn(signInDomainRequest: SignInDomainRequest): SignInResponse {
val user = userRepositorySpi.findByAccountIdAndStateWithCreated(signInDomainRequest.accountId)
Expand All @@ -37,13 +40,19 @@ class UserSignInApiImpl(
}

val accessToken = jwtTokenGeneratorSpi.generateJwtToken(signInDomainRequest.accountId, TokenType.ACCESS_TOKEN, params)
val expireAt = LocalDateTime.now().plusHours(jwtTokenGeneratorSpi.getAccessTokenExpirationAsHour().toLong())

val refreshToken = jwtTokenGeneratorSpi.generateJwtToken(signInDomainRequest.accountId, TokenType.REFRESH_TOKEN, params)
val expirationAt = LocalDateTime.now().plusHours(jwtTokenGeneratorSpi.getAccessTokenExpirationAsHour().toLong())
val refreshTokenDomain = RefreshToken(
tokenValue = refreshToken,
userId = user.id
)
refreshTokenSpi.saveRefreshToken(refreshTokenDomain)

return SignInResponse(
accessToken = accessToken,
refreshToken = refreshToken,
expireAt = expirationAt
expireAt = expireAt
)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.xquare.v1userservice.user.refreshtoken

import java.util.UUID

data class RefreshToken(
val tokenValue: String,
val userId: UUID
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.xquare.v1userservice.user.refreshtoken.spi

import com.xquare.v1userservice.user.refreshtoken.RefreshToken

interface RefreshTokenSpi {
suspend fun saveRefreshToken(refreshToken: RefreshToken): RefreshToken
}
1 change: 1 addition & 0 deletions user-infrastructure/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dependencies {
testImplementation(Dependencies.SPRING_MOCKK)
kapt(Dependencies.MAPSTRUCT_APT)
kapt(Dependencies.CONFIGURATION_PROCESSOR)
implementation(Dependencies.REACTIVE_REDIS)

implementation(project(":user-domain"))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.xquare.v1userservice.configuration.redis

import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory
import org.springframework.data.redis.core.ReactiveRedisOperations
import org.springframework.data.redis.core.ReactiveRedisTemplate
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.RedisSerializationContext
import org.springframework.data.redis.serializer.StringRedisSerializer

@Configuration
class RedisConfiguration {
@Primary
@Bean
fun redisOperations(factory: ReactiveRedisConnectionFactory): ReactiveRedisOperations<String, Any> {
val jsonSerializer = GenericJackson2JsonRedisSerializer()
val stringSerializer = StringRedisSerializer()

val serializationContext = RedisSerializationContext.newSerializationContext<String, Any>()
.key(stringSerializer)
.value(jsonSerializer)
.build()

return ReactiveRedisTemplate(factory, serializationContext)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.xquare.v1userservice.user.refreshtoken

import java.util.UUID

data class RefreshTokenEntity(
val tokenValue: String,
val userId: UUID
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.xquare.v1userservice.user.refreshtoken.exceptions

import com.xquare.v1userservice.exceptions.BaseException

class RefreshTokenSaveFailedException(
errorMessage: String
) : BaseException(errorMessage, 500)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.xquare.v1userservice.user.refreshtoken.mapper

import com.xquare.v1userservice.user.refreshtoken.RefreshToken
import com.xquare.v1userservice.user.refreshtoken.RefreshTokenEntity
import org.mapstruct.Mapper

@Mapper
interface RefreshTokenDomainMapper {
fun refreshTokenDomainToEntity(refreshToken: RefreshToken): RefreshTokenEntity
fun refreshTokenEntityToDomain(refreshTokenEntity: RefreshTokenEntity): RefreshToken
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.xquare.v1userservice.user.refreshtoken.spi

import com.xquare.v1userservice.user.refreshtoken.RefreshToken
import com.xquare.v1userservice.user.refreshtoken.exceptions.RefreshTokenSaveFailedException
import com.xquare.v1userservice.user.refreshtoken.mapper.RefreshTokenDomainMapper
import java.time.Duration
import kotlinx.coroutines.reactor.awaitSingle
import org.springframework.data.redis.core.ReactiveRedisOperations
import org.springframework.stereotype.Repository

@Repository
class RefreshTokenSpiImpl(
private val reactiveRedisOperations: ReactiveRedisOperations<String, Any>,
private val refreshTokenDomainMapper: RefreshTokenDomainMapper
) : RefreshTokenSpi {
override suspend fun saveRefreshToken(refreshToken: RefreshToken): RefreshToken {
val refreshTokenEntity = refreshTokenDomainMapper.refreshTokenDomainToEntity(refreshToken)
val isSaveSuccess = reactiveRedisOperations.opsForValue()
.set(refreshToken.tokenValue, refreshTokenEntity, Duration.ofDays(14)).awaitSingle()

if (!isSaveSuccess) {
throw RefreshTokenSaveFailedException("Refresh token save failed")
}

return refreshToken
}
}

0 comments on commit 1f5c79f

Please sign in to comment.