From a6ef53eba88fcfd444182af044339589510f14bf Mon Sep 17 00:00:00 2001 From: JuuuuHong Date: Fri, 20 Oct 2023 11:15:05 +0900 Subject: [PATCH 1/4] add :: reissue token serivce --- .../exception/InvalidRefreshTokenException.kt | 9 +++++++ .../RefreshTokenNotFoundException.kt | 8 ++++++ .../auth/exception/constant/AuthErrorCode.kt | 4 ++- .../msg/domain/auth/service/AuthService.kt | 1 + .../domain/auth/service/AuthServiceImpl.kt | 26 ++++++++++++++++++- .../auth/repository/RefreshTokenRepository.kt | 1 + 6 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/InvalidRefreshTokenException.kt create mode 100644 bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/RefreshTokenNotFoundException.kt diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/InvalidRefreshTokenException.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/InvalidRefreshTokenException.kt new file mode 100644 index 000000000..5212a4c60 --- /dev/null +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/InvalidRefreshTokenException.kt @@ -0,0 +1,9 @@ +package team.msg.domain.auth.exception + +import team.msg.domain.auth.exception.constant.AuthErrorCode +import team.msg.global.error.exception.BitgouelException + +class InvalidRefreshTokenException( + message: String +) : BitgouelException(message, AuthErrorCode.INVALID_TOKEN.status){ +} \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/RefreshTokenNotFoundException.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/RefreshTokenNotFoundException.kt new file mode 100644 index 000000000..210059816 --- /dev/null +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/RefreshTokenNotFoundException.kt @@ -0,0 +1,8 @@ +package team.msg.domain.auth.exception + +import team.msg.domain.auth.exception.constant.AuthErrorCode +import team.msg.global.error.exception.BitgouelException + +class RefreshTokenNotFoundException( + message: String +) : BitgouelException(message, AuthErrorCode.REFRESH_TOKEN_NOT_FOUND.status) \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/constant/AuthErrorCode.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/constant/AuthErrorCode.kt index 7d203d4f4..187a3b2bd 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/constant/AuthErrorCode.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/constant/AuthErrorCode.kt @@ -7,5 +7,7 @@ enum class AuthErrorCode( ALREADY_EXIST_EMAIL("이미 가입된 이메일입니다.", 409), ALREADY_EXIST_PHONE_NUMBER("이미 가입된 전화번호입니다.", 409), MISMATCH_PASSWORD("일치하지 않는 비밀번호입니다.", 401), - UNAPPROVED_USER("아직 회원가입 대기 상태입니다.", 401) + UNAPPROVED_USER("아직 회원가입 대기 상태입니다.", 401), + INVALID_TOKEN("유효하지 않은 토큰입니다.", 401), + REFRESH_TOKEN_NOT_FOUND("존재하지 않는 리프레시 토큰입니다.", 404) } \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthService.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthService.kt index d874994a8..13c86f351 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthService.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthService.kt @@ -10,4 +10,5 @@ interface AuthService { fun governmentSignUp(governmentSignUpRequest: GovernmentSignUpRequest) fun companyInstructorSignUp(companyInstructorSignUpRequest: CompanyInstructorSignUpRequest) fun login(request: LoginRequest): TokenResponse + fun reissueToken(request: String): TokenResponse } \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthServiceImpl.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthServiceImpl.kt index dae2b0162..947a590d6 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthServiceImpl.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthServiceImpl.kt @@ -1,15 +1,19 @@ package team.msg.domain.auth.service +import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import team.msg.common.enum.ApproveStatus import team.msg.common.util.SecurityUtil import team.msg.domain.auth.exception.AlreadyExistEmailException import team.msg.domain.auth.exception.AlreadyExistPhoneNumberException +import team.msg.domain.auth.exception.InvalidRefreshTokenException import team.msg.domain.auth.exception.MisMatchPasswordException +import team.msg.domain.auth.exception.RefreshTokenNotFoundException import team.msg.domain.auth.exception.UnApprovedUserException import team.msg.domain.auth.presentation.data.request.* import team.msg.domain.auth.presentation.data.response.TokenResponse +import team.msg.domain.auth.repository.RefreshTokenRepository import team.msg.domain.club.exception.ClubNotFoundException import team.msg.domain.club.model.Club import team.msg.domain.club.repository.ClubRepository @@ -32,6 +36,7 @@ import team.msg.domain.user.exception.UserNotFoundException import team.msg.domain.user.model.User import team.msg.domain.user.repository.UserRepository import team.msg.global.security.jwt.JwtTokenGenerator +import team.msg.global.security.jwt.JwtTokenParser import java.util.* @Service @@ -45,7 +50,9 @@ class AuthServiceImpl( private val professorRepository: ProfessorRepository, private val governmentRepository: GovernmentRepository, private val companyInstructorRepository: CompanyInstructorRepository, - private val jwtTokenGenerator: JwtTokenGenerator + private val jwtTokenGenerator: JwtTokenGenerator, + private val jwtTokenParser: JwtTokenParser, + private val refreshTokenRepository: RefreshTokenRepository ) : AuthService { /** @@ -189,6 +196,23 @@ class AuthServiceImpl( return jwtTokenGenerator.generateToken(user.id, user.authority) } + /** + * 토큰 재발급을 처리하는 메서드입니다. + * @param refreshToken + */ + override fun reissueToken(request: String): TokenResponse { + val refreshToken = jwtTokenParser.parseRefreshToken(request) + ?: throw InvalidRefreshTokenException("유효하지 않은 리프레시 토큰입니다. info : [ refreshToken = $request ]") + + val token = refreshTokenRepository.findByIdOrNull(refreshToken) + ?: throw RefreshTokenNotFoundException("존재하지 않는 리프레시 토큰입니다. info : [ refreshToken = $refreshToken ]") + + val user = userRepository.findByIdOrNull(token.userId) + ?: throw UserNotFoundException("존재하지 않는 유저입니다. info : [ userId = ${token.userId} ]") + + return jwtTokenGenerator.generateToken(user.id, user.authority) + } + /** * 유저 생성과 검증을 처리하는 private 메서드입니다. * @param email, name, phoneNumber, password, authority diff --git a/bitgouel-domain/src/main/kotlin/team/msg/domain/auth/repository/RefreshTokenRepository.kt b/bitgouel-domain/src/main/kotlin/team/msg/domain/auth/repository/RefreshTokenRepository.kt index 0b0eab6b6..75d17d866 100644 --- a/bitgouel-domain/src/main/kotlin/team/msg/domain/auth/repository/RefreshTokenRepository.kt +++ b/bitgouel-domain/src/main/kotlin/team/msg/domain/auth/repository/RefreshTokenRepository.kt @@ -4,4 +4,5 @@ import org.springframework.data.repository.CrudRepository import team.msg.domain.auth.model.RefreshToken interface RefreshTokenRepository : CrudRepository { + } \ No newline at end of file From a0455bcf12e7c883a7fbc8b3e31ac68a543fc227 Mon Sep 17 00:00:00 2001 From: JuuuuHong Date: Fri, 20 Oct 2023 11:46:52 +0900 Subject: [PATCH 2/4] update :: request name --- .../team/msg/domain/auth/presentation/AuthController.kt | 8 ++++++++ .../kotlin/team/msg/domain/auth/service/AuthService.kt | 2 +- .../team/msg/domain/auth/service/AuthServiceImpl.kt | 6 +++--- .../kotlin/team/msg/domain/auth/model/RefreshToken.kt | 5 +++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/presentation/AuthController.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/presentation/AuthController.kt index 29c6f1a8c..63a7f5a67 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/presentation/AuthController.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/presentation/AuthController.kt @@ -3,8 +3,10 @@ package team.msg.domain.auth.presentation import javax.validation.Valid import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.PatchMapping import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestHeader import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController import team.msg.domain.auth.mapper.AuthRequestMapper @@ -53,4 +55,10 @@ class AuthController( val response = authService.login(authRequestMapper.loginWebRequestToDto(request)) return ResponseEntity.ok(response) } + + @PatchMapping + fun reissueToken(@RequestHeader("RefreshToken") refreshToken: String): ResponseEntity { + val response = authService.reissueToken(refreshToken) + return ResponseEntity.ok(response) + } } \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthService.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthService.kt index 13c86f351..3518960d9 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthService.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthService.kt @@ -10,5 +10,5 @@ interface AuthService { fun governmentSignUp(governmentSignUpRequest: GovernmentSignUpRequest) fun companyInstructorSignUp(companyInstructorSignUpRequest: CompanyInstructorSignUpRequest) fun login(request: LoginRequest): TokenResponse - fun reissueToken(request: String): TokenResponse + fun reissueToken(refreshToken: String): TokenResponse } \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthServiceImpl.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthServiceImpl.kt index 947a590d6..526842fbb 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthServiceImpl.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/service/AuthServiceImpl.kt @@ -200,9 +200,9 @@ class AuthServiceImpl( * 토큰 재발급을 처리하는 메서드입니다. * @param refreshToken */ - override fun reissueToken(request: String): TokenResponse { - val refreshToken = jwtTokenParser.parseRefreshToken(request) - ?: throw InvalidRefreshTokenException("유효하지 않은 리프레시 토큰입니다. info : [ refreshToken = $request ]") + override fun reissueToken(refreshToken: String): TokenResponse { + val refreshToken = jwtTokenParser.parseRefreshToken(refreshToken) + ?: throw InvalidRefreshTokenException("유효하지 않은 리프레시 토큰입니다. info : [ refreshToken = $refreshToken ]") val token = refreshTokenRepository.findByIdOrNull(refreshToken) ?: throw RefreshTokenNotFoundException("존재하지 않는 리프레시 토큰입니다. info : [ refreshToken = $refreshToken ]") diff --git a/bitgouel-domain/src/main/kotlin/team/msg/domain/auth/model/RefreshToken.kt b/bitgouel-domain/src/main/kotlin/team/msg/domain/auth/model/RefreshToken.kt index f2a44f303..a5cb30adf 100644 --- a/bitgouel-domain/src/main/kotlin/team/msg/domain/auth/model/RefreshToken.kt +++ b/bitgouel-domain/src/main/kotlin/team/msg/domain/auth/model/RefreshToken.kt @@ -5,9 +5,10 @@ import org.springframework.data.redis.core.RedisHash import org.springframework.data.redis.core.TimeToLive import team.msg.domain.user.enums.Authority import java.util.UUID +import java.util.concurrent.TimeUnit @RedisHash("refresh_token") -class RefreshToken( +data class RefreshToken( @Id val token: String, @@ -15,6 +16,6 @@ class RefreshToken( val authority: Authority, - @TimeToLive + @TimeToLive(unit = TimeUnit.SECONDS) val expiredAt: Int ) \ No newline at end of file From 20968e9631cfdbf7965a501db14fa1cbffda4b51 Mon Sep 17 00:00:00 2001 From: JuuuuHong Date: Fri, 20 Oct 2023 11:47:14 +0900 Subject: [PATCH 3/4] =?UTF-8?q?add=20::=20reissue=20=ED=86=A0=ED=81=B0=20a?= =?UTF-8?q?pi=20=EC=A0=91=EA=B7=BC=EA=B6=8C=ED=95=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/team/msg/global/security/SecurityConfig.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/bitgouel-api/src/main/kotlin/team/msg/global/security/SecurityConfig.kt b/bitgouel-api/src/main/kotlin/team/msg/global/security/SecurityConfig.kt index c8fb5c1fe..fb624343e 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/global/security/SecurityConfig.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/global/security/SecurityConfig.kt @@ -40,6 +40,7 @@ class SecurityConfig( .mvcMatchers(HttpMethod.POST, "/auth/government").permitAll() .mvcMatchers(HttpMethod.POST, "/auth/company-instructor").permitAll() .mvcMatchers(HttpMethod.POST, "/auth/login").permitAll() + .mvcMatchers(HttpMethod.PATCH, "/auth").permitAll() .anyRequest().authenticated() .and() From bbdd8146ad60e72ff08907f89cba2d3bb68afd94 Mon Sep 17 00:00:00 2001 From: JuuuuHong Date: Fri, 20 Oct 2023 11:59:23 +0900 Subject: [PATCH 4/4] update :: unapproved user exception http status --- .../team/msg/domain/auth/exception/constant/AuthErrorCode.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/constant/AuthErrorCode.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/constant/AuthErrorCode.kt index 187a3b2bd..2a2ddd506 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/constant/AuthErrorCode.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/auth/exception/constant/AuthErrorCode.kt @@ -7,7 +7,7 @@ enum class AuthErrorCode( ALREADY_EXIST_EMAIL("이미 가입된 이메일입니다.", 409), ALREADY_EXIST_PHONE_NUMBER("이미 가입된 전화번호입니다.", 409), MISMATCH_PASSWORD("일치하지 않는 비밀번호입니다.", 401), - UNAPPROVED_USER("아직 회원가입 대기 상태입니다.", 401), + UNAPPROVED_USER("아직 회원가입 대기 상태입니다.", 403), INVALID_TOKEN("유효하지 않은 토큰입니다.", 401), REFRESH_TOKEN_NOT_FOUND("존재하지 않는 리프레시 토큰입니다.", 404) } \ No newline at end of file