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 |