Skip to content
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

[FEAT] JWT 기반 소셜 로그인 기능 구현 #15

Merged
merged 20 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c70064e
⚡️: fix Swagger, ApiResponseDto 패키지 수정 #14
orijoon98 Feb 16, 2024
5a71449
⚡️: fix PluApiApplication 패키지 수정 #14
orijoon98 Feb 16, 2024
ac90cd2
⚡️: fix Jpa, Querydsl 설정 수정 #14
orijoon98 Feb 16, 2024
0e9002a
🔥: remove 중복 ControllerAdvice 제거 #14
orijoon98 Feb 16, 2024
6c5f558
✨: feat JWT 관련 기능 추가 #14
orijoon98 Feb 16, 2024
ec7e202
✨: feat JWT 토큰 생성 서비스 추가 #14
orijoon98 Feb 16, 2024
342d8dd
✨: feat 카카오 API 연동 #14
orijoon98 Feb 16, 2024
aba0d2f
✨: feat 카카오 회원가입 API 추가 #14
orijoon98 Feb 16, 2024
65e6286
✨: feat 카카오 로그인 API 추가 #14
orijoon98 Feb 16, 2024
945b92f
✨: feat 토큰 갱신 API 추가 #14
orijoon98 Feb 16, 2024
1128c06
✨: feat 회원 인증용 interceptor, resolver 추가 #14
orijoon98 Feb 16, 2024
3d96f66
✨: feat 로그아웃 API 추가 #14
orijoon98 Feb 16, 2024
1fa63ad
✨: feat 애플 로그인 기능 추가 #14
orijoon98 Feb 16, 2024
64e3e8c
⚡️: fix Redis Template 의존성 문제 해결 #14
orijoon98 Feb 17, 2024
e7176bf
⚡️: fix QuerydslConfig 의존성 주입 방식 수정 #14
orijoon98 Mar 1, 2024
8663d1c
♻️: refactor RedisTemplate wrapping 적용, class 명 수정 #14
orijoon98 Mar 1, 2024
59832a3
♻️: refactor TokenService 패키지 수정 및 RedisHandler 사용하도록 수정 #14
orijoon98 Mar 1, 2024
1751374
♻️: refactor 토큰 생성 후 전달 방식 개선 #14
orijoon98 Mar 1, 2024
4b5fe80
♻️: refactor 예외처리 방식 수정 #14
orijoon98 Mar 1, 2024
28c6c84
⚡️: fix 불필요한 어노테이션 제거 #14
orijoon98 Mar 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.th.plu.api.controller.auth

import com.th.plu.api.controller.auth.dto.request.LoginRequestDto
import com.th.plu.api.controller.auth.dto.request.SignupRequestDto
import com.th.plu.api.controller.auth.dto.request.TokenRequestDto
import com.th.plu.api.controller.auth.dto.response.TokenResponseDto
import com.th.plu.api.service.auth.AuthServiceProvider
import com.th.plu.api.service.auth.TokenService
Expand Down Expand Up @@ -41,4 +42,10 @@ class AuthController(
val tokenInfo = tokenService.createTokenInfo(memberId)
return ApiResponse.success(tokenInfo)
}

@Operation(summary = "토큰 갱신")
@PostMapping("/v1/auth/refresh")
fun reissue(@Valid @RequestBody request: TokenRequestDto): ApiResponse<TokenResponseDto> {
return ApiResponse.success(tokenService.reissueToken(request))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.th.plu.api.controller.auth.dto.request

import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.NotBlank

data class TokenRequestDto(

@field:Schema(description = "토큰 - accessToken", example = "eyJhbGciOiJIUzUxMiJ9.udnKnDSK08EuX56E5k-")
orijoon98 marked this conversation as resolved.
Show resolved Hide resolved
@field:NotBlank(message = "accessToken 을 입력해주세요.")
val accessToken: String,

@field:Schema(description = "토큰 - refreshToken", example = "eyJhbGciOiJIUzUxMiJ9.udnKnDSK08EuX56E5k-")
@field:NotBlank(message = "refreshToken 을 입력해주세요.")
val refreshToken: String
)
Original file line number Diff line number Diff line change
@@ -1,17 +1,57 @@
package com.th.plu.api.service.auth

import com.th.plu.api.controller.auth.dto.request.TokenRequestDto
import com.th.plu.api.controller.auth.dto.response.TokenResponseDto
import com.th.plu.api.service.member.MemberServiceUtils
import com.th.plu.common.constant.RedisKey
import com.th.plu.common.exception.code.ErrorCode
import com.th.plu.common.exception.model.UnauthorizedException
import com.th.plu.common.util.JwtUtils
import com.th.plu.domain.domain.member.repository.MemberRepository
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class TokenService(
private val jwtUtils: JwtUtils
private val jwtUtils: JwtUtils,
private val memberRepository: MemberRepository,
private val redisTemplate: RedisTemplate<String, Any>
) {
@Transactional
orijoon98 marked this conversation as resolved.
Show resolved Hide resolved
fun createTokenInfo(memberId: Long): TokenResponseDto {
val tokens: List<String> = jwtUtils.createTokenInfo(memberId)
return TokenResponseDto(accessToken = tokens[0], refreshToken = tokens[1])
}

@Transactional
orijoon98 marked this conversation as resolved.
Show resolved Hide resolved
fun reissueToken(request: TokenRequestDto): TokenResponseDto {
val memberId = jwtUtils.getMemberIdFromJwt(request.accessToken)
val member = MemberServiceUtils.findMemberById(memberRepository, memberId)
if (!jwtUtils.validateToken(request.refreshToken)) {
member.resetFcmToken()
throw UnauthorizedException(
ErrorCode.UNAUTHORIZED_EXCEPTION,
"주어진 리프레시 토큰 ${request.refreshToken} 이 유효하지 않습니다."
)
}
val refreshToken = redisTemplate.opsForValue().get(RedisKey.REFRESH_TOKEN + memberId) as String?
if (refreshToken == null) {
member.resetFcmToken()
throw UnauthorizedException(
ErrorCode.UNAUTHORIZED_EXCEPTION,
"이미 만료된 리프레시 토큰 ${request.refreshToken} 입니다."
)
}
if (refreshToken != request.refreshToken) {
jwtUtils.expireRefreshToken(member.id!!)
member.resetFcmToken()
throw UnauthorizedException(
ErrorCode.UNAUTHORIZED_EXCEPTION,
"해당 리프레시 토큰 ${request.refreshToken} 의 정보가 일치하지 않습니다."
)
}
val newTokens = jwtUtils.createTokenInfo(memberId)
return TokenResponseDto(accessToken = newTokens[0], refreshToken = newTokens[1])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ class MemberServiceUtils {
}
}

fun findMemberById(
memberRepository: MemberRepository,
id: Long
): Member {
return memberRepository.findMemberById(id)
?: throw NotFoundException(ErrorCode.NOT_FOUND_MEMBER_EXCEPTION, "존재하지 않는 유저 $id 입니다")
}

fun findMemberBySocialIdAndSocialType(
memberRepository: MemberRepository,
socialId: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ class JwtUtils(
}

fun getMemberIdFromJwt(accessToken: String): Long {
return parseClaims(accessToken).get(JwtKey.MEMBER_ID, Long::class.java)
val memberId = parseClaims(accessToken)[JwtKey.MEMBER_ID] as Int
return memberId.toLong()
}

private fun parseClaims(accessToken: String): Claims {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ class Member(
@Enumerated(EnumType.STRING)
var role: MemberRole,

@Column(name = "fcm_token", nullable = false, length = 300)
var fcmToken: String,
@Column(name = "fcm_token", nullable = true, length = 300)
var fcmToken: String?,

@OneToOne(fetch = FetchType.LAZY, orphanRemoval = true, cascade = [CascadeType.ALL])
@JoinColumn(name = "setting_id", nullable = false)
Expand Down Expand Up @@ -63,4 +63,8 @@ class Member(
fun updateFcmToken(fcmToken: String) {
this.fcmToken = fcmToken
}

fun resetFcmToken() {
this.fcmToken = null
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.th.plu.external.sqs.dto

data class FirebaseMessageDto(val type: MessageType, val fcmToken: String, val title: String, val body: String) :
data class FirebaseMessageDto(val type: MessageType, val fcmToken: String?, val title: String, val body: String) :
SqsMessageDto(type) {
}