Skip to content

Commit

Permalink
redis 의존성 제거하기 (#108)
Browse files Browse the repository at this point in the history
* refactor: #107 refreshTokenRepository 인터페이스화해 의존성 제거

* feat: JpaRefreshtoken entity, repository를 infra-security에 추가, rdbRefreshToken 구현

rdbRefreshTokenRepository는 만료시간에 대해 추가적인 구현 필요

* feat: #107 jpaRefreshToken에 생성일 추가

* feat: jpaRefreshToken 시간 체크 기능 추가

* fix: 어노테이션 수정
  • Loading branch information
KarmaPol authored Jul 29, 2024
1 parent 4fe095b commit 69d3847
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 18 deletions.
11 changes: 11 additions & 0 deletions infra/infra-security/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ bootJar.enabled = false
// 외부에서 의존하기 위한 jar로 생성하는 옵션, main이 없는 라이브러리에서는 true로 비활성화함
jar.enabled = true

plugins {
kotlin("plugin.jpa") version "1.9.22" // JPA를 사용하기 위한 플러그인
kotlin("kapt")
}

allOpen {
annotation("javax.persistence.Entity")
annotation("javax.persistence.MappedSuperclass")
annotation("javax.persistence.Embeddable")
}

dependencies {
implementation(project(":data"))
implementation(project(":infra:infra-redis"))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.kw.infrasecurity.config

import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaRepositories

@Configuration
@EntityScan("com.kw.infrasecurity.refreshToken")
@EnableJpaRepositories(basePackages = ["com.kw.infrasecurity.refreshToken"])
class JpaConfig {
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package com.kw.infrasecurity.oauth

import com.kw.data.domain.member.Member
import com.kw.data.domain.member.repository.MemberRepository
import com.kw.infraredis.repository.RedisRefreshTokenRepository
import com.kw.infrasecurity.jwt.JwtTokenProvider
import com.kw.infrasecurity.refreshToken.repository.RefreshTokenRepository
import com.kw.infrasecurity.util.HttpResponseUtil
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
Expand All @@ -18,10 +18,10 @@ import java.util.*
@Component
@Transactional
class OAuth2SuccessHandler(
private val jwtTokenProvider: JwtTokenProvider,
private val memberRepository: MemberRepository,
private val redisRefreshTokenRepository: RedisRefreshTokenRepository,
private val httpResponseUtil: HttpResponseUtil
private val jwtTokenProvider: JwtTokenProvider,
private val memberRepository: MemberRepository,
private val refreshTokenRepository: RefreshTokenRepository,
private val httpResponseUtil: HttpResponseUtil
) : AuthenticationSuccessHandler {

override fun onAuthenticationSuccess(
Expand Down Expand Up @@ -53,7 +53,7 @@ class OAuth2SuccessHandler(
val refreshToken = jwtTokenProvider.generateRefreshToken()


redisRefreshTokenRepository.save(refreshToken = refreshToken, memberId = member.id!!)
refreshTokenRepository.save(refreshToken = refreshToken, memberId = member.id!!)
member.updateLastLoggedInAt()

httpResponseUtil.writeResponse(response!!, accessToken, refreshToken, isSignUp, member.nickname)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.kw.infrasecurity.refreshToken.entity

import jakarta.persistence.*
import org.springframework.data.annotation.CreatedDate
import org.springframework.data.jpa.domain.support.AuditingEntityListener
import java.time.LocalDateTime

@Entity
@Table(name = "refresh_token")
@EntityListeners(AuditingEntityListener::class)
class JpaRefreshToken(@Column(name = "refresh_token", nullable = false, updatable = false)
val refreshToken: String,
@Column(name = "member_id", nullable = false, updatable = false)
val memberId: Long) {
@Id
@Column(name = "id", nullable = false, updatable = false)
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long? = null

@Column(name = "created_at", nullable = false, updatable = false)
@CreatedDate
var createdAt: LocalDateTime = LocalDateTime.now()
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.kw.infrasecurity.refreshToken.repository

import com.kw.infrasecurity.refreshToken.entity.JpaRefreshToken
import org.springframework.data.jpa.repository.JpaRepository

interface JpaRefreshTokenRepository: JpaRepository<JpaRefreshToken, Long> {
fun deleteByRefreshToken(refreshToken: String)

fun findByRefreshToken(refreshToken: String): JpaRefreshToken
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.kw.infrasecurity.refreshToken.repository

import com.kw.infrasecurity.refreshToken.entity.JpaRefreshToken
import org.springframework.context.annotation.Primary
import org.springframework.stereotype.Repository
import java.time.Duration
import java.time.LocalDateTime

@Primary
@Repository
class RdbRefreshTokenRepository(val jpaRefreshTokenRepository: JpaRefreshTokenRepository): RefreshTokenRepository {
override fun save(refreshToken: String, memberId: Long) {
val jpaRefreshToken = JpaRefreshToken(
refreshToken = refreshToken,
memberId = memberId)
jpaRefreshTokenRepository.save(jpaRefreshToken)
}

override fun delete(refreshToken: String) {
jpaRefreshTokenRepository.deleteByRefreshToken(refreshToken)
}

override fun findByRefreshToken(refreshToken: String): Long? {
val jpaRefreshToken = jpaRefreshTokenRepository.findByRefreshToken(refreshToken) ?: return null

if(isRefreshTokenExpired(jpaRefreshToken))
return null

return jpaRefreshToken.memberId
}
// TODO 테스트 해보기

private fun isRefreshTokenExpired(refreshToken: JpaRefreshToken): Boolean {
val timeDifference = Duration.between(LocalDateTime.now(), refreshToken.createdAt)
if(timeDifference.toSeconds() < REFRESH_TOKEN_EXPIRE_LONG) {
return false
}
return true
}

// TODO 만료된 refresh token 처리 구현하기
companion object {
private const val REFRESH_TOKEN_EXPIRE_LONG = 259200L
}
// TODO 만료기간 yml에서 가져오게 수정하기
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.kw.infraredis.repository
package com.kw.infrasecurity.refreshToken.repository

import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.core.ValueOperations
Expand All @@ -7,18 +7,18 @@ import java.util.concurrent.TimeUnit


@Repository
class RedisRefreshTokenRepository(val redisTemplate: RedisTemplate<String, Any>) {
fun save(refreshToken: String, memberId: Long) {
class RedisRefreshTokenRepository(val redisTemplate: RedisTemplate<String, Any>): RefreshTokenRepository {
override fun save(refreshToken: String, memberId: Long) {
val valueOperations: ValueOperations<String, Any> = redisTemplate.opsForValue()
valueOperations[refreshToken] = memberId
redisTemplate.expire(refreshToken, REFRESH_TOKEN_EXPIRE_LONG, TimeUnit.SECONDS)
}

fun delete(refreshToken: String) {
override fun delete(refreshToken: String) {
redisTemplate.delete(refreshToken)
}

fun findByRefreshToken(refreshToken: String): Long? {
override fun findByRefreshToken(refreshToken: String): Long? {
val memberId = redisTemplate.opsForValue().get(refreshToken) ?: return null
return memberId.toString().toLong()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.kw.infrasecurity.refreshToken.repository

interface RefreshTokenRepository {
fun save(refreshToken: String, memberId: Long)

fun delete(refreshToken: String)

fun findByRefreshToken(refreshToken: String): Long?
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,34 @@ import com.kw.api.domain.auth.dto.request.RefreshTokenRequest
import com.kw.api.domain.auth.dto.response.TokenResponse
import com.kw.data.domain.member.Member
import com.kw.data.domain.member.repository.MemberRepository
import com.kw.infraredis.repository.RedisRefreshTokenRepository
import com.kw.infrasecurity.jwt.JwtTokenProvider
import com.kw.infrasecurity.oauth.OAuth2UserDetails
import com.kw.infrasecurity.refreshToken.repository.RefreshTokenRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.stereotype.Service

@Service
class AuthService(
val redisRefreshTokenRepository: RedisRefreshTokenRepository,
val memberRepository: MemberRepository,
val jwtTokenProvider: JwtTokenProvider
val refreshTokenRepository: RefreshTokenRepository,
val memberRepository: MemberRepository,
val jwtTokenProvider: JwtTokenProvider
) {

fun logout(refreshTokenRequest: RefreshTokenRequest) {
redisRefreshTokenRepository.delete(refreshTokenRequest.refreshToken)
refreshTokenRepository.delete(refreshTokenRequest.refreshToken)
}

fun refreshAccessToken(refreshTokenRequest: RefreshTokenRequest): TokenResponse {
val memberId = redisRefreshTokenRepository.findByRefreshToken(refreshTokenRequest.refreshToken)
val memberId = refreshTokenRepository.findByRefreshToken(refreshTokenRequest.refreshToken)
?: throw ApiException(ApiErrorCode.REFRESH_TOKEN_EXPIRED)
val member = memberRepository.findByIdOrNull(memberId)
?: throw ApiException(ApiErrorCode.REFRESH_TOKEN_NOT_FOUND_MEMBER)

val oauth2UserDetails = createOauth2UserDetails(member)
val accessToken = jwtTokenProvider.generateAccessToken(oauth2UserDetails)
val refreshToken = jwtTokenProvider.generateRefreshToken()
redisRefreshTokenRepository.save(refreshToken, memberId)
refreshTokenRepository.save(refreshToken, memberId)

return TokenResponse(accessToken, refreshToken)
}
Expand Down

0 comments on commit 69d3847

Please sign in to comment.