-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature: 로그아웃 api #119
The head ref may contain hidden characters: "118-feature-\uB85C\uADF8\uC544\uC6C3-api"
feature: 로그아웃 api #119
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
깔끔 코드 맛집👍
src/main/kotlin/com/petqua/application/auth/AuthFacadeService.kt
Outdated
Show resolved
Hide resolved
Test Results 42 files +1 42 suites +1 18s ⏱️ -1s Results for commit eea6e24. ± Comparison against base commit ad5b8fc. This pull request removes 5 and adds 11 tests. Note that renamed tests count towards both.
♻️ This comment has been updated with latest results. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
redis 를 잘 쓰시는 게 너무 멋져요!
저도 열심히 공부하겠습니다 😃
고생하셨어요!
fun save(memberId: Long, accessToken: String) { | ||
redisTemplate.opsForValue().set( | ||
blackListKeyByMemberId(memberId), | ||
accessToken, | ||
authTokenProperties.accessTokenLiveTime, | ||
TimeUnit.MILLISECONDS, | ||
) | ||
} | ||
|
||
fun isBlackListed(memberId: Long, accessToken: String): Boolean { | ||
val blackListToken = redisTemplate.opsForValue().get(blackListKeyByMemberId(memberId)) | ||
return blackListToken == accessToken | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BlackList 신기하네요~ 👍
만약 redis 가 없다면 이런 기능은 DB에 따로 저장해서 구현하려나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분에 대해서 조금 고민이 들더라고요.
말씀대로 redis는 캐시의 구현체로서 의미가 있어서 만약 redis가 없다면 DB에 따로 저장해야 할 것 같아요.
현재는 추상화 없이 세부 구현체로 직접 구현 되어 있습니다.
@Cachable
을 사용하려 했는데 ttl 설정이 어려워서 redisTemplate으로 구현했어요.
좀 더 분석해보고 ttl을 지정하면서 @cachable 처럼 추상화 된 캐시스토리지도 만들어 보겠습니다!
@@ -23,6 +23,7 @@ class AuthFacadeService( | |||
} | |||
|
|||
fun extendLogin(accessToken: String, refreshToken: String): AuthTokenInfo { | |||
authService.validateTokenExpiredStatusForExtendLogin(accessToken, refreshToken) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
로그인 연장 시에는 로그아웃한 accessToken 이 사용될 수 있는 것처럼 보여요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
버그 발견! 감사합니다👍
fun signOut() { | ||
oauthAccessToken = DELETED_AUTH_FIELD | ||
oauthAccessTokenExpiresAt = null | ||
oauthRefreshToken = DELETED_AUTH_FIELD | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
깔끔합니다 👍
@Combi153 @hgo641 문제 상황현재 구현되어 있는 상태는 로그아웃 요청 시 redis에 해당 로그아웃 요청에 사용된 토큰을 추가하는데요.
이런 형태로 저장합니다. 유효한 accessToken에 대해서, payload를 얻은 뒤 포함되어 있는 그런데, extendLogin 처럼 accessToken 값 자체만 사용하는 경우에는 문제가 있더라고요. 고민두 가지 방법이 있습니다.
이런식으로 value를 모두 가져온 후, 해당 token이 블랙리스트인지 확인합니다. 고려되는 문제점은 scan에 대한 복잡도와
추가적으로, 크게 고려할 사항은 아니지만 scan의 경우 조회 일관성을 보장하지 않을 수도 있습니다.
로그아웃된 토큰만 저장하는 방식이에요! 단점(?)은 회원 이력을 남기지 못한다는 것과 설계 변경으로 인해 PR내 로직을 다수 수정해야합니다..! 의견 주시면 감사하겠습니다! |
@TaeyeonRoyce fun getExpiredTokenPayload(token: String): Map<String, String> {
return try {
getPayload(token)
} catch (e: ExpiredJwtException) {
val payload = mutableMapOf<String, String>()
e.claims.forEach { payload[it.key] = it.value.toString() }
payload
} catch (e: Exception) {
throw IllegalArgumentException()
}
} 만료된 토큰에 대해서 payload 를 접근하는 방법이 아예 없는 건 아니더라구요! 위 코드처럼 ExpiredJwtException 을 Catch 한 블럭에서 e.claims 로 claim 에 접근할 수 있습니다. 이 정보로 payload를 구성할 수 있어요! @SpringBootTest(webEnvironment = NONE)
class JwtProviderTest(
private val jwtProvider: JwtProvider,
) : StringSpec({
"만료 토큰 생성" {
val token = jwtProvider.createToken(
claims = mapOf(
"memberId" to "1",
"authority" to "GUEST"
),
tokenLiveTime = 0,
issuedDate = Date()
)
jwtProvider.isExpiredToken(token) shouldBe true
}
"만료 토큰 payload 얻기" {
val token = jwtProvider.createToken(
claims = mapOf(
"memberId" to "1",
"authority" to "GUEST"
),
tokenLiveTime = 0,
issuedDate = Date()
)
val payload = jwtProvider.getExpiredTokenPayload(token)
payload["memberId"] shouldBe "1"
payload["authority"] shouldBe "GUEST"
}
"만료 토큰과 정상 토큰의 차이" {
val expiredToken = jwtProvider.createToken(
claims = mapOf(
"memberId" to "1",
"authority" to "GUEST"
),
tokenLiveTime = 0,
issuedDate = Date()
)
val token = jwtProvider.createToken(
claims = mapOf(
"memberId" to "1",
"authority" to "GUEST"
),
tokenLiveTime = 100000000000,
issuedDate = Date()
)
val expiredTokenPayload = jwtProvider.getExpiredTokenPayload(expiredToken)
val payload = jwtProvider.getPayload(token)
println("만료 토큰 payload")
println(expiredTokenPayload)
println("토큰 payload")
println(payload)
}
}) 위 테스트로 만료된 토큰의 payload 를 받아오는 것이 충분히 가능하다는 것을 확인했습니다. 출력 결과만 보아도 차이가 없어 보이네요! redis 는 기존과 같이 key-value 구조를 이어가면서 extendLogin 에서는 만료된 토큰 payload 를 추출해서 key로 value 를 조회하는 건 어떠신가요? value로 찾거나, value 만 저장하는 방식보다 데이터를 효율적으로 사용할 수 있을 것 같아요. |
@Combi153 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
로그아웃 고생하셨습니다!
blacklist 관리까지 추가해주셔서 더 신뢰도 높은 애플리케이션이 되었네요
감사합니다~~ 😀
fun validateBlacklistTokenRegardlessExpiration(token: String) { | ||
val accessTokenClaims = getAccessTokenClaimsRegardlessExpiration(token) | ||
validateBlackListed(accessTokenClaims.memberId, token) | ||
} | ||
|
||
private fun getAccessTokenClaimsRegardlessExpiration(token: String): AccessTokenClaims { | ||
return try { | ||
AccessTokenClaims.from(jwtProvider.getPayload(token)) | ||
} catch (e: ExpiredJwtException) { | ||
val payload = mutableMapOf<String, String>() | ||
e.claims.forEach { payload[it.key] = it.value.toString() } | ||
AccessTokenClaims.from(payload) | ||
} catch (e: JwtException) { | ||
throw AuthException(INVALID_ACCESS_TOKEN) | ||
} catch (e: NullPointerException) { | ||
throw AuthException(INVALID_ACCESS_TOKEN) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋아요!!!👍 👍
* feat: 로그아웃 시 회원 데이터 변경 로직 추가 * feat: 로그아웃 로직 추가 * feat: 로그아웃 API 추가 * feat: 블랙리스트 토큰 캐시 스토리지 추가 * feat: 로그아웃 시 사용중인 토큰 블랙리스트 추가 로직 적용 * feat: 인증 요청에 대한 블랙리스트 토큰 조회 로직 추가 * test: Redis cleaner 방식 수정 * refactor: 일관성 있는 method 이름으로 수정 * refactor: 사용 하지 않는 파라미터 제거 * fix: 토큰만 사용 되는 API 요청에 대한 로그아웃 블랙리스트 검증 로직 추가
* refactor: Money 값객체 생성 및 적용 * refactor: OrderPayment에 status 추가 * refactor: ProductSnapshot 생성 및 적용 * refactor: OrderService 메서드 분리 * hotfix: prod환경 redis connection 정보 추가 * feature: 알림 조회 및 읽음 처리 api (#114) * feat: 알림 도메인 추가 * feat: 알림 전체 조회 기능 추가 * feat: 알림 전체 조회 API 추가 * feat: 읽지 않은 알림 개수 조회 로직 추가 * feat: 읽지 않은 알림 개수 조회 API 추가 * feat: 알림 확인 로직 추가 * feat: 알림 확인 API 추가 * style: Annotation 순서 수정 * refactor: query method 네이밍 수정 * test: 검증부 추가 * chore: 알림 관련 더미 데이터 추가 * fix: 봉달 상품 수정시 수량을 포함 하여 중복 검사 하도록 수정 (#117) * refactor: 스웨거 명세 추가 * refactor: 상품 상세 조회, 장바구니 조회시 storeId를 반환하게 변경 * refactor: DeliveryGroupKey 값객체 생성 * refactor: ProductSnapshotException을 ProductException으로 변경 * refactor: 메서드 이름 변경 * refactor: 초기 데이터에 ProductSnapshot 저장 추가 * refactor: 메서드명 변경 * refactor: OrderShippingAddress를 nullable로 변경 * refactor: 최근 ProductSnapshot들만 조회하게 변경 * refactor: OrderProducts 검증 로직 분리 * feature: 로그아웃 api (#119) * feat: 로그아웃 시 회원 데이터 변경 로직 추가 * feat: 로그아웃 로직 추가 * feat: 로그아웃 API 추가 * feat: 블랙리스트 토큰 캐시 스토리지 추가 * feat: 로그아웃 시 사용중인 토큰 블랙리스트 추가 로직 적용 * feat: 인증 요청에 대한 블랙리스트 토큰 조회 로직 추가 * test: Redis cleaner 방식 수정 * refactor: 일관성 있는 method 이름으로 수정 * refactor: 사용 하지 않는 파라미터 제거 * fix: 토큰만 사용 되는 API 요청에 대한 로그아웃 블랙리스트 검증 로직 추가 * feature: 회원가입 api 개발 (#120) * feat: Fish entity 설계 * feat: Fish repository 조회 기능 추가 * feat: FishService 조회 기능 추가 * feat: FishController 어종 자동완성 검색 API 추가 * feat: FishTankName 검증 기능 추가 * feat: FishTank 추가 * feat: FishLifeYear 검증 기능 추가 * feat: PetFish 기능 추가 * refactor: 카카오 프로필 받지 않도록 변경 * feat: MemberDetail 추가 * chore: rebase 충돌 해결 * refactor: 어종 이름 자동완성 검색 로직 수정 * refactor: FishTank memberId 필드명 변경 * feat: Member update 메서드 추가 * feat: PetFishSex, TankSize 정적 팩터리 메서드 추가 * feat: PetFish 일급컬렉션 PetFishes 추가 * feat: BannedWord 객체 추가 * feat: NicknameWord 객체 추가 * feat: Nickname 객체 추가 * feat: NicknameGenerator generate 기능 추가 * chore: rebase 충돌 해결 * refactor: 로그인 api 상태코드 변경 - 회원가입이 필요한지 여부를 상태코드로 분기 * chore: rebase 충돌 해결 * chore: rebase 충돌 해결 * refactor: 랜덤 닉네임 설정 시 숫자 생성 정책 반영 * feat: 이름에 금지 단어 포함 여부 검증 기능 추가 * feat: 회원 물생활 프로필 입력 API 추가 * feat: 이름 검증 API 추가 * chore: rebase 충돌 해결 * refactor: 회원가입 토큰 커스텀 헤더 관리 기능 추가, 스웨거 작업 * fix: 예외 처리 테스트 기댓값 알맞게 수정 * chore: rebase 충돌 해결 * fix: 회원 삭제 로직 memberId 로 동작하도록 변경 * refactor: 회원 랜덤 닉네임 생성 시 시도 횟수 제한 * refactor: AuthMember 이름 AuthCredentials 로 변경 * refactor: 회원가입 API, 이름 검증 API 응답 상태코드 변경 * chore: rebase 충돌 해결 * refactor: 불필요한 로직 및 메서드 삭제 * refactor: 랜덤 닉네임 생성 실패 시 예외 코드 변경 * fix: Fish 개수를 통한 검증 로직 수정 * refactor: Fish 자동완성 검색 시 정렬 순서 변경 * fix: 이전 PR에 AuthCredentials, Member에 관한 코드 변경사항 반영 * refactor: 로그인 토큰과 회원가입 토큰을 AuthToken 으로 추상화 * refactor: ProductSnapshot 생성 및 적용 * refactor: 스웨거 설정 추가 및 사용하지 않는 필드 제거 * refactor: 리스트 필드에 스웨거 설정 추가 * refactor: 긴 로직을 변수로 분리 * refactor: 주문 배송지 유효성 검사 로직 수정 * refactor: 주문 상품 존재 유효성 검증 로직 변경 * refactor: 주문 옵션 유효성 검증 로직 변경 * refactor: OrderProducts 유효성 검사를 하나의 메서드로 묶어 제공 * refactor: 메서드 이름 변경 * refactor: 예외 코드 변경 * refactor: OrderProducts 유효성 검증 로직 추가 * refactor: 메서드 이름 변경 * refactor: Order의 status 삭제 * test: OrderPayment 테스트 추가 * refactor: OrderProducts 검증 로직 수정 * test: Order Controller 테스트 추가 * refactor: Order 조회 시 목록으로 조회되도록 수정 * refactor: OrderException 타입 에러 수정 * refactor: 예외 코드 이름 변경 --------- Co-authored-by: TaeyeonRoyce <[email protected]> Co-authored-by: Taeyeon <[email protected]> Co-authored-by: Chanmin Ju(Hu chu) <[email protected]>
📌 관련 이슈
📁 작업 설명
로그아웃 시, 회원의 oauth 토큰 정보와 리프레시 토큰을 초기화 하였습니다.
추가로 사용중이던 인증정보에 대해
사용할 수 없는(블랙리스트)
인증 정보로 관리 되도록 구성하였습니다.기타
블랙리스트 토큰 관리를 위해 Redis에 인증 정보들을 저장하였습니다.
저장 형식은 아래와 같습니다.
만료 시간은 accessToken의 만료시간과 동일하게 구성하였습니다!