diff --git a/src/main/kotlin/com/tinuproject/tinu/domain/exception/token/InvalidedToken.kt b/src/main/kotlin/com/tinuproject/tinu/domain/exception/token/InvalidedTokenException.kt similarity index 85% rename from src/main/kotlin/com/tinuproject/tinu/domain/exception/token/InvalidedToken.kt rename to src/main/kotlin/com/tinuproject/tinu/domain/exception/token/InvalidedTokenException.kt index 162d52a..ab51882 100644 --- a/src/main/kotlin/com/tinuproject/tinu/domain/exception/token/InvalidedToken.kt +++ b/src/main/kotlin/com/tinuproject/tinu/domain/exception/token/InvalidedTokenException.kt @@ -2,6 +2,6 @@ package com.tinuproject.tinu.domain.exception.token import com.tinuproject.tinu.domain.exception.base.BaseException -class InvalidedToken( +class InvalidedTokenException( ):BaseException(tokenErrorCode=TokenErrorCode.TOKEN_INVALIDED) {} \ No newline at end of file diff --git a/src/main/kotlin/com/tinuproject/tinu/domain/token/Tokens.kt b/src/main/kotlin/com/tinuproject/tinu/domain/token/Tokens.kt new file mode 100644 index 0000000..dd044ad --- /dev/null +++ b/src/main/kotlin/com/tinuproject/tinu/domain/token/Tokens.kt @@ -0,0 +1,8 @@ +package com.tinuproject.tinu.domain.token + +class Tokens( + val accessToken : String, + val refreshToken : String +) { + +} \ No newline at end of file diff --git a/src/main/kotlin/com/tinuproject/tinu/domain/token/refreshtoken/RefreshTokenController.kt b/src/main/kotlin/com/tinuproject/tinu/domain/token/refreshtoken/RefreshTokenController.kt new file mode 100644 index 0000000..37b467b --- /dev/null +++ b/src/main/kotlin/com/tinuproject/tinu/domain/token/refreshtoken/RefreshTokenController.kt @@ -0,0 +1,54 @@ +package com.tinuproject.tinu.domain.token.refreshtoken + +import com.tinuproject.tinu.domain.exception.token.InvalidedTokenException +import com.tinuproject.tinu.domain.exception.token.NotFoundTokenException +import com.tinuproject.tinu.domain.token.Tokens +import com.tinuproject.tinu.domain.token.refreshtoken.service.RefreshTokenService +import com.tinuproject.tinu.web.CookieGenerator +import jakarta.servlet.http.HttpServletResponse +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Value +import org.springframework.http.ResponseEntity +import org.springframework.stereotype.Controller +import org.springframework.web.bind.annotation.CookieValue +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping + +@Controller +@RequestMapping("api/token") +class RefreshTokenController( + private val refreshTokenService : RefreshTokenService, + + @Value("\${cookie.token.access-token}") + private val accessTokenKey : String, + + @Value("\${cookie.token.refresh-token}") + private val refreshTokenkey : String +) { + var log : Logger = LoggerFactory.getLogger(this::class.java) + + + @GetMapping("/generate") + fun generateAccessToken(httpServletResponse: HttpServletResponse, @CookieValue(name = "RefreshToken") refreshToken : String?): ResponseEntity> { + var tokens : Tokens + log.info("AccessToken 갱신 시도") + if(refreshToken==null){ + throw NotFoundTokenException() + } + try{ + tokens = refreshTokenService.ReissueAccessTokenByRefreshToken(refreshToken) + }catch (e : InvalidedTokenException){ + throw e + } + + httpServletResponse.addCookie(CookieGenerator.createCookies(accessTokenKey,tokens.accessToken)) + httpServletResponse.addCookie(CookieGenerator.createCookies(refreshTokenkey, tokens.refreshToken)) + var body : MutableMap = mutableMapOf() + + body.put("Tokens",tokens) + + var responseEntity : ResponseEntity> = ResponseEntity.ok().body(body) + return responseEntity + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tinuproject/tinu/security/RefreshTokenRepository.kt b/src/main/kotlin/com/tinuproject/tinu/domain/token/refreshtoken/repository/RefreshTokenRepository.kt similarity index 88% rename from src/main/kotlin/com/tinuproject/tinu/security/RefreshTokenRepository.kt rename to src/main/kotlin/com/tinuproject/tinu/domain/token/refreshtoken/repository/RefreshTokenRepository.kt index 78aa044..248aefc 100644 --- a/src/main/kotlin/com/tinuproject/tinu/security/RefreshTokenRepository.kt +++ b/src/main/kotlin/com/tinuproject/tinu/domain/token/refreshtoken/repository/RefreshTokenRepository.kt @@ -1,4 +1,4 @@ -package com.tinuproject.tinu.security +package com.tinuproject.tinu.domain.token.refreshtoken.repository import com.tinuproject.tinu.domain.entity.RefreshToken import jakarta.persistence.ManyToOne @@ -8,7 +8,6 @@ import org.springframework.data.repository.CrudRepository import org.springframework.stereotype.Repository import java.util.* -@Repository interface RefreshTokenRepository:CrudRepository { fun findByUserId(userId : UUID) : RefreshToken diff --git a/src/main/kotlin/com/tinuproject/tinu/domain/token/refreshtoken/service/RefreshTokenService.kt b/src/main/kotlin/com/tinuproject/tinu/domain/token/refreshtoken/service/RefreshTokenService.kt new file mode 100644 index 0000000..35664bc --- /dev/null +++ b/src/main/kotlin/com/tinuproject/tinu/domain/token/refreshtoken/service/RefreshTokenService.kt @@ -0,0 +1,9 @@ +package com.tinuproject.tinu.domain.token.refreshtoken.service + +import com.tinuproject.tinu.domain.token.Tokens +import org.springframework.stereotype.Service + + +interface RefreshTokenService { + fun ReissueAccessTokenByRefreshToken(refreshToken : String) : Tokens +} \ No newline at end of file diff --git a/src/main/kotlin/com/tinuproject/tinu/domain/token/refreshtoken/service/RefreshTokenServiceImpl.kt b/src/main/kotlin/com/tinuproject/tinu/domain/token/refreshtoken/service/RefreshTokenServiceImpl.kt new file mode 100644 index 0000000..d3256eb --- /dev/null +++ b/src/main/kotlin/com/tinuproject/tinu/domain/token/refreshtoken/service/RefreshTokenServiceImpl.kt @@ -0,0 +1,53 @@ +package com.tinuproject.tinu.domain.token.refreshtoken.service + +import com.tinuproject.tinu.domain.entity.RefreshToken +import com.tinuproject.tinu.domain.exception.token.InvalidedTokenException +import com.tinuproject.tinu.domain.token.Tokens +import com.tinuproject.tinu.domain.token.refreshtoken.repository.RefreshTokenRepository +import com.tinuproject.tinu.security.jwt.JwtUtil +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Service +import java.security.SignatureException +import java.util.* + +@Service +class RefreshTokenServiceImpl( + @Value("\${jwt.refresh-token.expiration-time}") + private val REFRESH_TOKEN_EXPIRATION_TIME: Long, // 리프레쉬 토큰 유효기간 + + @Value("\${jwt.access-token.expiration-time}") + private val ACCESS_TOKEN_EXPIRATION_TIME: Long, // 액세스 토큰 유효기간 + + private val refreshTokenRepository: RefreshTokenRepository, + private val jwtUtil : JwtUtil, +):RefreshTokenService { + var log : Logger = LoggerFactory.getLogger(this::class.java) + + override fun ReissueAccessTokenByRefreshToken(refreshToken: String): Tokens { + try{ + jwtUtil.validateToken(refreshToken) + }catch (e : SignatureException){ + throw InvalidedTokenException() + } + + val userId :UUID = UUID.fromString(jwtUtil.getUserIdFromToken(refreshToken)) + + //리프레쉬 토큰 삭제 + refreshTokenRepository.deleteByUserId(userId) + + //리프레쉬 토큰 재발행. + val token = jwtUtil.generateRefreshToken(userId,REFRESH_TOKEN_EXPIRATION_TIME) + + val newRefreshToken = RefreshToken(userId = userId, token = token) + + //리프레쉬 토큰 저장 + refreshTokenRepository.save(newRefreshToken) + + //AccesToken 재발행. + val accessToken : String = jwtUtil.generateAccessToken(userId, ACCESS_TOKEN_EXPIRATION_TIME) + + return Tokens(accessToken=accessToken, refreshToken = refreshToken) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/tinuproject/tinu/security/config/SecurityConfig.kt b/src/main/kotlin/com/tinuproject/tinu/security/config/SecurityConfig.kt index 6dafab3..2aae17f 100644 --- a/src/main/kotlin/com/tinuproject/tinu/security/config/SecurityConfig.kt +++ b/src/main/kotlin/com/tinuproject/tinu/security/config/SecurityConfig.kt @@ -36,7 +36,7 @@ class SecurityConfig( return WebSecurityCustomizer { web: WebSecurity -> //로그인이 아예 안되어 있어도 괜찮은 api //해당 ""에 API 추가시 해당 API는 필터를 거치지 않음. -// web.ignoring().requestMatchers("") + web.ignoring().requestMatchers("/api/token/**") } } diff --git a/src/main/kotlin/com/tinuproject/tinu/security/jwt/JwtUtil.kt b/src/main/kotlin/com/tinuproject/tinu/security/jwt/JwtUtil.kt index c8de7f7..0ffa4cc 100644 --- a/src/main/kotlin/com/tinuproject/tinu/security/jwt/JwtUtil.kt +++ b/src/main/kotlin/com/tinuproject/tinu/security/jwt/JwtUtil.kt @@ -95,7 +95,7 @@ class JwtUtil { }catch(e : SignatureException){ log.warn("Claim이 유효하지 않은 토큰입니다.{}",e.message) //TODO(유효하지 않는 토큰 처리") - throw Exception() + throw e }catch (e : Exception){ log.warn("토큰과 관련한 예기치 못한 에러가 발생했습니다.") //TODO(예상치 못한 오류 에러로 처리) diff --git a/src/main/kotlin/com/tinuproject/tinu/security/oauth2/handler/OAuthLoginSuccessHandler.kt b/src/main/kotlin/com/tinuproject/tinu/security/oauth2/handler/OAuthLoginSuccessHandler.kt index c96e7b7..84208ee 100644 --- a/src/main/kotlin/com/tinuproject/tinu/security/oauth2/handler/OAuthLoginSuccessHandler.kt +++ b/src/main/kotlin/com/tinuproject/tinu/security/oauth2/handler/OAuthLoginSuccessHandler.kt @@ -4,7 +4,7 @@ import com.tinuproject.tinu.domain.entity.RefreshToken import com.tinuproject.tinu.domain.entity.SocialMember import com.tinuproject.tinu.domain.enum.Social import com.tinuproject.tinu.domain.socialmember.repository.SocialMemberRepository -import com.tinuproject.tinu.security.RefreshTokenRepository +import com.tinuproject.tinu.domain.token.refreshtoken.repository.RefreshTokenRepository import com.tinuproject.tinu.security.jwt.JwtUtil import com.tinuproject.tinu.security.oauth2.dto.KakaoUserInfo import com.tinuproject.tinu.security.oauth2.dto.NaverUserInfo @@ -31,7 +31,7 @@ class OAuthLoginSuccessHandler( private val jwtUtil: JwtUtil, - private val userRepository: SocialMemberRepository , + private val userRepository: SocialMemberRepository, private val refreshTokenRepository: RefreshTokenRepository, diff --git a/src/main/kotlin/com/tinuproject/tinu/web/CookieGenerator.kt b/src/main/kotlin/com/tinuproject/tinu/web/CookieGenerator.kt new file mode 100644 index 0000000..9b36ec5 --- /dev/null +++ b/src/main/kotlin/com/tinuproject/tinu/web/CookieGenerator.kt @@ -0,0 +1,17 @@ +package com.tinuproject.tinu.web + +import jakarta.servlet.http.Cookie + +class CookieGenerator { + companion object{ + fun createCookies(key : String, value : String) : Cookie { + val cookie = Cookie(key, value) + cookie.path = "/" + cookie.isHttpOnly = false + cookie.secure = false + cookie.maxAge = 3600 + + return cookie + } + } +} \ No newline at end of file