From ad41802198577907696a8a3bdaaa556b38ade238 Mon Sep 17 00:00:00 2001 From: ta2ye0n Date: Fri, 1 Nov 2024 14:00:51 +0900 Subject: [PATCH 1/7] add :: RefreshToken --- .../auth/domain/entity/RefreshToken.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/com/gcms/v3/domain/auth/domain/entity/RefreshToken.java diff --git a/src/main/java/com/gcms/v3/domain/auth/domain/entity/RefreshToken.java b/src/main/java/com/gcms/v3/domain/auth/domain/entity/RefreshToken.java new file mode 100644 index 0000000..beba913 --- /dev/null +++ b/src/main/java/com/gcms/v3/domain/auth/domain/entity/RefreshToken.java @@ -0,0 +1,20 @@ +package com.gcms.v3.domain.auth.domain.entity; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.index.Indexed; + +@Getter +@Builder +@RedisHash(value = "refreshToken", timeToLive = 60L * 60 * 24 * 7) +public class RefreshToken { + + @Indexed + private String email; + + @Id + @Indexed + private String token; +} From 864263037e2cf93022e66f18ca9f224d90054ad4 Mon Sep 17 00:00:00 2001 From: ta2ye0n Date: Fri, 1 Nov 2024 14:00:56 +0900 Subject: [PATCH 2/7] add :: RefreshToken save --- .../auth/service/impl/SignInServiceImpl.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gcms/v3/domain/auth/service/impl/SignInServiceImpl.java b/src/main/java/com/gcms/v3/domain/auth/service/impl/SignInServiceImpl.java index a3974e5..b350f99 100644 --- a/src/main/java/com/gcms/v3/domain/auth/service/impl/SignInServiceImpl.java +++ b/src/main/java/com/gcms/v3/domain/auth/service/impl/SignInServiceImpl.java @@ -1,5 +1,7 @@ package com.gcms.v3.domain.auth.service.impl; +import com.gcms.v3.domain.auth.domain.entity.RefreshToken; +import com.gcms.v3.domain.auth.domain.repository.RefreshTokenRepository; import com.gcms.v3.domain.auth.presentation.data.request.SignInRequestDto; import com.gcms.v3.domain.auth.presentation.data.response.TokenInfoResponseDto; import com.gcms.v3.domain.auth.service.SignInService; @@ -24,6 +26,7 @@ public class SignInServiceImpl implements SignInService { private final UserRepository userRepository; private final JwtTokenProvider jwtTokenProvider; private final UserRoleRepository userRoleRepository; + private final RefreshTokenRepository refreshTokenRepository; public TokenInfoResponseDto execute(SignInRequestDto signInRequestDto) { String accessToken = oAuth2Service.requestAccessToken(signInRequestDto.code()); @@ -32,7 +35,11 @@ public TokenInfoResponseDto execute(SignInRequestDto signInRequestDto) { User user = userRepository.findByEmail(googleOAuth2UserInfo.getEmail()) .orElseGet(() -> toEntity(googleOAuth2UserInfo)); - return jwtTokenProvider.generateToken(user.getEmail()); + TokenInfoResponseDto responseDto = jwtTokenProvider.generateToken(user.getEmail()); + + saveRefreshToken(user.getEmail(), responseDto.refreshToken()); + + return responseDto; } private User toEntity(GoogleOAuth2UserInfo googleOAuth2UserInfo) { @@ -55,4 +62,12 @@ private void saveAuthority(User user) { userRoleRepository.save(userRole); } + private void saveRefreshToken(String email, String refreshToken) { + RefreshToken token = RefreshToken.builder() + .email(email) + .token(refreshToken) + .build(); + + refreshTokenRepository.save(token); + } } From 401d4b5e36024843c19c51b606c7d03b400e4835 Mon Sep 17 00:00:00 2001 From: ta2ye0n Date: Fri, 1 Nov 2024 14:01:17 +0900 Subject: [PATCH 3/7] add :: ExpiredTokenException --- .../domain/auth/exception/ExpiredTokenException.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/java/com/gcms/v3/domain/auth/exception/ExpiredTokenException.java diff --git a/src/main/java/com/gcms/v3/domain/auth/exception/ExpiredTokenException.java b/src/main/java/com/gcms/v3/domain/auth/exception/ExpiredTokenException.java new file mode 100644 index 0000000..dcd2141 --- /dev/null +++ b/src/main/java/com/gcms/v3/domain/auth/exception/ExpiredTokenException.java @@ -0,0 +1,10 @@ +package com.gcms.v3.domain.auth.exception; + +import com.gcms.v3.global.error.BasicException; +import com.gcms.v3.global.error.ErrorCode; + +public class ExpiredTokenException extends BasicException { + public ExpiredTokenException() { + super(ErrorCode.EXPIRED_TOKEN); + } +} From 1f57ac7058c30bc5a86d00877b70e9c8a7d07144 Mon Sep 17 00:00:00 2001 From: ta2ye0n Date: Fri, 1 Nov 2024 14:01:27 +0900 Subject: [PATCH 4/7] add :: RefreshTokenRepository --- .../domain/repository/RefreshTokenRepository.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/com/gcms/v3/domain/auth/domain/repository/RefreshTokenRepository.java diff --git a/src/main/java/com/gcms/v3/domain/auth/domain/repository/RefreshTokenRepository.java b/src/main/java/com/gcms/v3/domain/auth/domain/repository/RefreshTokenRepository.java new file mode 100644 index 0000000..69f3ae6 --- /dev/null +++ b/src/main/java/com/gcms/v3/domain/auth/domain/repository/RefreshTokenRepository.java @@ -0,0 +1,12 @@ +package com.gcms.v3.domain.auth.domain.repository; + +import com.gcms.v3.domain.auth.domain.entity.RefreshToken; +import org.springframework.data.repository.CrudRepository; + +import java.util.Optional; + +public interface RefreshTokenRepository extends CrudRepository { + Optional findByToken(String token); + + RefreshToken findByEmail(String email); +} From 933cc9c92315668ebe0b248607409e5d34e6abda Mon Sep 17 00:00:00 2001 From: ta2ye0n Date: Fri, 1 Nov 2024 14:01:40 +0900 Subject: [PATCH 5/7] feat :: ReissueTokenService --- .../auth/service/ReissueTokenService.java | 7 +++ .../service/impl/ReissueTokenServiceImpl.java | 49 +++++++++++++++++++ .../global/security/jwt/JwtTokenProvider.java | 22 +++++++++ 3 files changed, 78 insertions(+) create mode 100644 src/main/java/com/gcms/v3/domain/auth/service/ReissueTokenService.java create mode 100644 src/main/java/com/gcms/v3/domain/auth/service/impl/ReissueTokenServiceImpl.java diff --git a/src/main/java/com/gcms/v3/domain/auth/service/ReissueTokenService.java b/src/main/java/com/gcms/v3/domain/auth/service/ReissueTokenService.java new file mode 100644 index 0000000..d74c45c --- /dev/null +++ b/src/main/java/com/gcms/v3/domain/auth/service/ReissueTokenService.java @@ -0,0 +1,7 @@ +package com.gcms.v3.domain.auth.service; + +import com.gcms.v3.domain.auth.presentation.data.response.TokenInfoResponseDto; + +public interface ReissueTokenService { + TokenInfoResponseDto execute(String refreshToken); +} diff --git a/src/main/java/com/gcms/v3/domain/auth/service/impl/ReissueTokenServiceImpl.java b/src/main/java/com/gcms/v3/domain/auth/service/impl/ReissueTokenServiceImpl.java new file mode 100644 index 0000000..8281444 --- /dev/null +++ b/src/main/java/com/gcms/v3/domain/auth/service/impl/ReissueTokenServiceImpl.java @@ -0,0 +1,49 @@ +package com.gcms.v3.domain.auth.service.impl; + +import com.gcms.v3.domain.auth.domain.entity.RefreshToken; +import com.gcms.v3.domain.auth.domain.repository.RefreshTokenRepository; +import com.gcms.v3.domain.auth.exception.UserNotFoundException; +import com.gcms.v3.domain.auth.presentation.data.response.TokenInfoResponseDto; +import com.gcms.v3.domain.auth.service.ReissueTokenService; +import com.gcms.v3.domain.user.domain.entity.User; +import com.gcms.v3.domain.user.domain.repository.UserRepository; +import com.gcms.v3.global.security.exception.ExpiredTokenException; +import com.gcms.v3.global.security.jwt.JwtTokenProvider; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class ReissueTokenServiceImpl implements ReissueTokenService { + + private final JwtTokenProvider jwtTokenProvider; + private final UserRepository userRepository; + private final RefreshTokenRepository refreshTokenRepository; + + public TokenInfoResponseDto execute(String refreshToken) { + String refresh = jwtTokenProvider.parseToken(refreshToken); + + String email = jwtTokenProvider.exactEmailFromRefreshToken(refresh); + + User user = userRepository.findByEmail(email) + .orElseThrow(UserNotFoundException::new); + + RefreshToken existingRefreshToken = refreshTokenRepository.findByToken(refresh) + .orElseThrow(ExpiredTokenException::new); + + TokenInfoResponseDto responseDto = jwtTokenProvider.generateToken(user.getEmail()); + + saveRefreshToken(existingRefreshToken.getEmail(), responseDto.refreshToken()); + + return responseDto; + } + + private void saveRefreshToken(String email, String refreshToken) { + RefreshToken token = RefreshToken.builder() + .email(email) + .token(refreshToken) + .build(); + + refreshTokenRepository.save(token); + } +} diff --git a/src/main/java/com/gcms/v3/global/security/jwt/JwtTokenProvider.java b/src/main/java/com/gcms/v3/global/security/jwt/JwtTokenProvider.java index dd7e0ab..302bd08 100644 --- a/src/main/java/com/gcms/v3/global/security/jwt/JwtTokenProvider.java +++ b/src/main/java/com/gcms/v3/global/security/jwt/JwtTokenProvider.java @@ -126,4 +126,26 @@ public boolean validateToken(String token) { } } + public String parseToken(String token) { + if (token.startsWith(TOKEN_PREFIX)) { + return token.replace(TOKEN_PREFIX, ""); + } + else return null; + } + + public String exactEmailFromRefreshToken(String refreshToken) { + return getTokenSubject(refreshToken, refreshtokenkey); + } + + private String getTokenSubject (String token, Key secret) { + return getTokenBody(token, secret).getSubject(); + } + + private Claims getTokenBody(String token, Key secret) { + return Jwts.parserBuilder() + .setSigningKey(secret) + .build() + .parseClaimsJws(token) + .getBody(); + } } From 8a4f58587a2bc23af30463177e5ffa0a3ee9d7d8 Mon Sep 17 00:00:00 2001 From: ta2ye0n Date: Fri, 1 Nov 2024 14:01:54 +0900 Subject: [PATCH 6/7] =?UTF-8?q?add=20::=20endpoint=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gcms/v3/domain/auth/presentation/AuthController.java | 8 ++++++++ .../gcms/v3/global/security/config/SecurityConfig.java | 1 + 2 files changed, 9 insertions(+) diff --git a/src/main/java/com/gcms/v3/domain/auth/presentation/AuthController.java b/src/main/java/com/gcms/v3/domain/auth/presentation/AuthController.java index 3196e4e..0e78609 100644 --- a/src/main/java/com/gcms/v3/domain/auth/presentation/AuthController.java +++ b/src/main/java/com/gcms/v3/domain/auth/presentation/AuthController.java @@ -2,6 +2,7 @@ import com.gcms.v3.domain.auth.presentation.data.request.SignInRequestDto; import com.gcms.v3.domain.auth.presentation.data.response.TokenInfoResponseDto; +import com.gcms.v3.domain.auth.service.ReissueTokenService; import com.gcms.v3.domain.auth.service.SignInService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -13,10 +14,17 @@ public class AuthController { private final SignInService signInService; + private final ReissueTokenService reissueTokenService; @PostMapping public ResponseEntity signIn (@RequestBody SignInRequestDto signInRequestDto) { TokenInfoResponseDto res = signInService.execute(signInRequestDto); return ResponseEntity.ok(res); } + + @PostMapping("/reissueToken") + public ResponseEntity reissueToken (@RequestBody String refreshToken) { + TokenInfoResponseDto res = reissueTokenService.execute(refreshToken); + return ResponseEntity.ok(res); + } } diff --git a/src/main/java/com/gcms/v3/global/security/config/SecurityConfig.java b/src/main/java/com/gcms/v3/global/security/config/SecurityConfig.java index f4a717d..64d13fe 100644 --- a/src/main/java/com/gcms/v3/global/security/config/SecurityConfig.java +++ b/src/main/java/com/gcms/v3/global/security/config/SecurityConfig.java @@ -43,6 +43,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .authorizeHttpRequests((authorizeRequests) -> authorizeRequests .requestMatchers(HttpMethod.POST, "/v3/auth").permitAll() + .requestMatchers(HttpMethod.POST, "/v3/auth/reissueToken").authenticated() ) .addFilterBefore(new ExceptionFilter(objectMapper), UsernamePasswordAuthenticationFilter.class) From ebbcf15dc50beeebb4c3a2080a83e1a63296186b Mon Sep 17 00:00:00 2001 From: ta2ye0n Date: Fri, 1 Nov 2024 14:58:36 +0900 Subject: [PATCH 7/7] refactor :: Long -> String --- .../domain/auth/domain/repository/RefreshTokenRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/gcms/v3/domain/auth/domain/repository/RefreshTokenRepository.java b/src/main/java/com/gcms/v3/domain/auth/domain/repository/RefreshTokenRepository.java index 69f3ae6..08172e1 100644 --- a/src/main/java/com/gcms/v3/domain/auth/domain/repository/RefreshTokenRepository.java +++ b/src/main/java/com/gcms/v3/domain/auth/domain/repository/RefreshTokenRepository.java @@ -5,7 +5,7 @@ import java.util.Optional; -public interface RefreshTokenRepository extends CrudRepository { +public interface RefreshTokenRepository extends CrudRepository { Optional findByToken(String token); RefreshToken findByEmail(String email);