실무 프로젝트로 배우는 Kotlin & Spring/회원 인증 서비스 개발하기

로그인과 로그아웃

webmaster 2022. 12. 15. 18:38
728x90

로그인

Controller

@PostMapping("/signin")
suspend fun signIn(@RequestBody signInRequest: SignInRequest): SignInResponse {
    return userService.signIn(signInRequest)
}

SignInRequest

data class SignInRequest(
    val email:String,
    val password: String,
)

SignInResponse

data class SignInResponse(
    val email: String,
    val username: String,
    val token: String,
)

Service

@Service
class UserService(
    private val userRepository: UserRepository,
    private val jwtProperties: JWTProperties,
    private val cacheManager: CoroutineCacheManager<User>,
) {
    companion object{
        private val CACHE_TTL = Duration.ofMinutes(1)
    }
    suspend fun signIn(signInRequest: SignInRequest):SignInResponse {
        return with(userRepository.findByEmail(signInRequest.email) ?: throw UserNotFoundException()){
            val verified = BCryptUtils.verity(signInRequest.password, password)
            if(!verified)
                throw PasswordNotMatchedException()
            val jwtClaim = JWTClaim(
                userId = id!!,
                email = email,
                profileUrl = profileUrl,
                username = username
            )
            val token = JWTUtils.createToken(claim = jwtClaim, properties = jwtProperties) //토큰을 서버에서 보관하고 있어야한다
            cacheManager.awaitPut(key = token, value = this, ttl = CACHE_TTL)
            SignInResponse(
                email = email,
                username = username,
                token = token
            )
        }
    }

}

CacheManager

@Component
class CoroutineCacheManager<T> {
    private val localCache = ConcurrentHashMap<String, CacheWrapper<T>>()

    suspend fun awaitPut(key: String, value: T, ttl: Duration){
        localCache[key] = CacheWrapper(cached = value, Instant.now().plusMillis(ttl.toMillis()))
    }

    suspend fun awaitEvict(key: String) {
        localCache.remove(key)
    }

    data class CacheWrapper<T>(val cached: T, val ttl: Instant)
}
  • JwtProperties, CacheManager를 빈으로 주입받는다.
    • JwtProperties는 yml 값을 읽어서 사용할 수 있게 해 준다.
    • CacheManager는 제네릭 타입을 받을 수 있으며(현재는 User타입만 사용) ConcurrentHashMap을 이용하여, ttl 시간까지 만큼만 값을 유지시켜 준다.
    • awaitPut 은 캐시에 값을 넣어주고, awaitEvict는 캐시에서 값을 제거시켜준다.
    • CacheWarpper를 사용해, 실제 데이터를 CacheWrapper로 감싸준다.
  • DB에 저장된 암호화된 password와 비교하여, 만약 통과한다면, token을 발행하고, 캐시에 저장한 뒤, 반환한다.

로그아웃

Controller

@DeleteMapping("/logout")
@ResponseStatus(HttpStatus.NO_CONTENT)
suspend fun logout(@AuthToken token: String){
    userService.logout(token)
}

Service

suspend fun logout(token: String) {
    cacheManager.awaitEvict(token)//캐시를 삭제하는 함수
}
  • awaitEvict 함수를 사용해 케시에서 제거해 준다.
728x90

'실무 프로젝트로 배우는 Kotlin & Spring > 회원 인증 서비스 개발하기' 카테고리의 다른 글

리포트 조회 / 내정보 수정  (0) 2022.12.16
내정보 조회  (0) 2022.12.16
회원가입  (0) 2022.12.14
JWT 기반 인증 구현  (0) 2022.12.13
토큰 기반 인증  (0) 2022.12.13