From 466d5eddbfe524fe8410b029075e67afb9c47d10 Mon Sep 17 00:00:00 2001 From: SJ70 Date: Thu, 25 Jul 2024 20:27:44 +0900 Subject: [PATCH 01/11] =?UTF-8?q?chore:=20redis=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../j9/bestmoments/config/RedisConfig.java | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/main/java/com/j9/bestmoments/config/RedisConfig.java diff --git a/build.gradle b/build.gradle index 0a471e2..1703438 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,7 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' implementation 'com.google.auth:google-auth-library-oauth2-http:1.4.0' implementation 'com.google.cloud:google-cloud-storage:2.1.5' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' diff --git a/src/main/java/com/j9/bestmoments/config/RedisConfig.java b/src/main/java/com/j9/bestmoments/config/RedisConfig.java new file mode 100644 index 0000000..abc000d --- /dev/null +++ b/src/main/java/com/j9/bestmoments/config/RedisConfig.java @@ -0,0 +1,23 @@ +package com.j9.bestmoments.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; + +@Configuration +public class RedisConfig { + + @Value("${redis.host}") + private String host; + + @Value("${redis.port}") + private int port; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + return new LettuceConnectionFactory(host, port); + } + +} \ No newline at end of file From f07e88dea30f72348f7c2baa97dbfe58a8296a70 Mon Sep 17 00:00:00 2001 From: SJ70 Date: Thu, 25 Jul 2024 20:33:09 +0900 Subject: [PATCH 02/11] =?UTF-8?q?feat:=20=EC=83=88=EB=A1=9C=EC=9A=B4=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - @RedisHash 적용 - 토큰 만료 기간을 상수로 적용 --- .../constants/TokenExpiration.java | 11 +++++++++ .../j9/bestmoments/domain/AccessToken.java | 13 +++++++++++ .../j9/bestmoments/domain/RefreshToken.java | 13 +++++++++++ .../com/j9/bestmoments/domain/Token_.java | 23 +++++++++++++++++++ .../repository/Token_Repository.java | 10 ++++++++ .../j9/bestmoments/service/Token_Service.java | 13 +++++++++++ 6 files changed, 83 insertions(+) create mode 100644 src/main/java/com/j9/bestmoments/constants/TokenExpiration.java create mode 100644 src/main/java/com/j9/bestmoments/domain/AccessToken.java create mode 100644 src/main/java/com/j9/bestmoments/domain/RefreshToken.java create mode 100644 src/main/java/com/j9/bestmoments/domain/Token_.java create mode 100644 src/main/java/com/j9/bestmoments/repository/Token_Repository.java create mode 100644 src/main/java/com/j9/bestmoments/service/Token_Service.java diff --git a/src/main/java/com/j9/bestmoments/constants/TokenExpiration.java b/src/main/java/com/j9/bestmoments/constants/TokenExpiration.java new file mode 100644 index 0000000..eddcbcb --- /dev/null +++ b/src/main/java/com/j9/bestmoments/constants/TokenExpiration.java @@ -0,0 +1,11 @@ +package com.j9.bestmoments.constants; + +public final class TokenExpiration { + + // 10분 + public static final int ACCESS_TOKEN = 60 * 10; + + // 60분 + public static final int REFRESH_TOKEN = 60 * 60; + +} diff --git a/src/main/java/com/j9/bestmoments/domain/AccessToken.java b/src/main/java/com/j9/bestmoments/domain/AccessToken.java new file mode 100644 index 0000000..2494067 --- /dev/null +++ b/src/main/java/com/j9/bestmoments/domain/AccessToken.java @@ -0,0 +1,13 @@ +package com.j9.bestmoments.domain; + +import com.j9.bestmoments.constants.TokenExpiration; +import org.springframework.data.redis.core.RedisHash; + +@RedisHash(value = "AccessToken", timeToLive = TokenExpiration.ACCESS_TOKEN) +public class AccessToken extends Token_ { + + public AccessToken(Member member, String token) { + super(member, token); + } + +} diff --git a/src/main/java/com/j9/bestmoments/domain/RefreshToken.java b/src/main/java/com/j9/bestmoments/domain/RefreshToken.java new file mode 100644 index 0000000..015ee1f --- /dev/null +++ b/src/main/java/com/j9/bestmoments/domain/RefreshToken.java @@ -0,0 +1,13 @@ +package com.j9.bestmoments.domain; + +import com.j9.bestmoments.constants.TokenExpiration; +import org.springframework.data.redis.core.RedisHash; + +@RedisHash(value = "RefreshToken", timeToLive = TokenExpiration.REFRESH_TOKEN) +public class RefreshToken extends Token_ { + + public RefreshToken(Member member, String token) { + super(member, token); + } + +} diff --git a/src/main/java/com/j9/bestmoments/domain/Token_.java b/src/main/java/com/j9/bestmoments/domain/Token_.java new file mode 100644 index 0000000..ccdd392 --- /dev/null +++ b/src/main/java/com/j9/bestmoments/domain/Token_.java @@ -0,0 +1,23 @@ +package com.j9.bestmoments.domain; + +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import java.util.UUID; +import lombok.Getter; +import org.springframework.data.redis.core.RedisHash; + +@Getter +@NoArgsConstructor +@RedisHash("Token") +public class Token_ { + + @Id + private String token; + private UUID memberId; + + public Token_(Member member, String token) { + this.token = token; + this.memberId = member.getId(); + } + +} diff --git a/src/main/java/com/j9/bestmoments/repository/Token_Repository.java b/src/main/java/com/j9/bestmoments/repository/Token_Repository.java new file mode 100644 index 0000000..c0131eb --- /dev/null +++ b/src/main/java/com/j9/bestmoments/repository/Token_Repository.java @@ -0,0 +1,10 @@ +package com.j9.bestmoments.repository; + +import com.j9.bestmoments.domain.Token_; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface Token_Repository extends CrudRepository { + +} diff --git a/src/main/java/com/j9/bestmoments/service/Token_Service.java b/src/main/java/com/j9/bestmoments/service/Token_Service.java new file mode 100644 index 0000000..ccbc9f2 --- /dev/null +++ b/src/main/java/com/j9/bestmoments/service/Token_Service.java @@ -0,0 +1,13 @@ +package com.j9.bestmoments.service; + +import com.j9.bestmoments.repository.Token_Repository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class Token_Service { + + private final Token_Repository tokenRepository; + +} From b540dc22547712bb56e6c3ee7bd8f4ddfd7fbb51 Mon Sep 17 00:00:00 2001 From: SJ70 Date: Thu, 25 Jul 2024 20:41:51 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20=ED=99=94=EC=9D=B4=ED=8A=B8=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=97=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../j9/bestmoments/service/Token_Service.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/main/java/com/j9/bestmoments/service/Token_Service.java b/src/main/java/com/j9/bestmoments/service/Token_Service.java index ccbc9f2..2fac08b 100644 --- a/src/main/java/com/j9/bestmoments/service/Token_Service.java +++ b/src/main/java/com/j9/bestmoments/service/Token_Service.java @@ -1,13 +1,46 @@ package com.j9.bestmoments.service; +import com.j9.bestmoments.domain.AccessToken; +import com.j9.bestmoments.domain.Member; +import com.j9.bestmoments.domain.RefreshToken; +import com.j9.bestmoments.domain.Token; +import com.j9.bestmoments.domain.Token_; +import com.j9.bestmoments.dto.response.LoginDto; +import com.j9.bestmoments.jwt.JwtTokenProvider; import com.j9.bestmoments.repository.Token_Repository; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor +@Slf4j public class Token_Service { private final Token_Repository tokenRepository; + private final JwtTokenProvider jwtTokenProvider; + + @Transactional + public LoginDto create(Member member) { + String accessToken = createAccessToken(member); + String refreshToken = createRefreshToken(member); + log.info("토큰 발급됨\n - accessToken: {}\n - refreshToken: {}", accessToken, refreshToken); + return new LoginDto("Bearer", accessToken, refreshToken, member.getDeletedAt()); + } + + private String createAccessToken(Member member) { + String accessTokenValue = jwtTokenProvider.generateAccessToken(member); + AccessToken accessToken = new AccessToken(member, accessTokenValue); + tokenRepository.save(accessToken); + return accessTokenValue; + } + + private String createRefreshToken(Member member) { + String refreshTokenValue = jwtTokenProvider.generateRefreshToken(member); + RefreshToken refreshToken = new RefreshToken(member, refreshTokenValue); + tokenRepository.save(refreshToken); + return refreshTokenValue; + } } From 9cc3d1056bcf7f16715daa166f3e7f01311f50ed Mon Sep 17 00:00:00 2001 From: SJ70 Date: Thu, 25 Jul 2024 21:01:26 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=EA=B0=95?= =?UTF-8?q?=EC=A0=9C=20=EB=A7=8C=EB=A3=8C=20=EB=A9=94=EC=84=9C=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../j9/bestmoments/controller/AuthController.java | 1 + .../bestmoments/repository/Token_Repository.java | 2 ++ .../com/j9/bestmoments/service/Token_Service.java | 14 +++++++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/j9/bestmoments/controller/AuthController.java b/src/main/java/com/j9/bestmoments/controller/AuthController.java index 6a8942d..f57957a 100644 --- a/src/main/java/com/j9/bestmoments/controller/AuthController.java +++ b/src/main/java/com/j9/bestmoments/controller/AuthController.java @@ -7,6 +7,7 @@ import com.j9.bestmoments.domain.Member; import com.j9.bestmoments.service.MemberService; import com.j9.bestmoments.service.TokenService; +import com.j9.bestmoments.service.Token_Service; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/j9/bestmoments/repository/Token_Repository.java b/src/main/java/com/j9/bestmoments/repository/Token_Repository.java index c0131eb..ff98f58 100644 --- a/src/main/java/com/j9/bestmoments/repository/Token_Repository.java +++ b/src/main/java/com/j9/bestmoments/repository/Token_Repository.java @@ -7,4 +7,6 @@ @Repository public interface Token_Repository extends CrudRepository { + boolean existsByToken(String token); + } diff --git a/src/main/java/com/j9/bestmoments/service/Token_Service.java b/src/main/java/com/j9/bestmoments/service/Token_Service.java index 2fac08b..99eb5e8 100644 --- a/src/main/java/com/j9/bestmoments/service/Token_Service.java +++ b/src/main/java/com/j9/bestmoments/service/Token_Service.java @@ -3,13 +3,14 @@ import com.j9.bestmoments.domain.AccessToken; import com.j9.bestmoments.domain.Member; import com.j9.bestmoments.domain.RefreshToken; -import com.j9.bestmoments.domain.Token; import com.j9.bestmoments.domain.Token_; import com.j9.bestmoments.dto.response.LoginDto; import com.j9.bestmoments.jwt.JwtTokenProvider; import com.j9.bestmoments.repository.Token_Repository; +import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -43,4 +44,15 @@ private String createRefreshToken(Member member) { return refreshTokenValue; } + @Transactional + public void expire(String token) { + Token_ foundToken = this.findByToken(token); + tokenRepository.delete(foundToken); + } + + private Token_ findByToken(String token) { + return tokenRepository.findById(token) + .orElseThrow(() -> new AccessDeniedException("만료되거나 발급되지 않은 토큰입니다.")); + } + } From 4d80ff667eef54cdd2a5d26611ae26719ce08340 Mon Sep 17 00:00:00 2001 From: SJ70 Date: Thu, 25 Jul 2024 21:09:46 +0900 Subject: [PATCH 05/11] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=EC=9E=AC?= =?UTF-8?q?=EB=B0=9C=EA=B8=89=20=EB=A9=94=EC=84=9C=EB=93=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/j9/bestmoments/service/Token_Service.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/j9/bestmoments/service/Token_Service.java b/src/main/java/com/j9/bestmoments/service/Token_Service.java index 99eb5e8..41e998a 100644 --- a/src/main/java/com/j9/bestmoments/service/Token_Service.java +++ b/src/main/java/com/j9/bestmoments/service/Token_Service.java @@ -21,6 +21,7 @@ public class Token_Service { private final Token_Repository tokenRepository; private final JwtTokenProvider jwtTokenProvider; + private final MemberService memberService; @Transactional public LoginDto create(Member member) { @@ -55,4 +56,12 @@ private Token_ findByToken(String token) { .orElseThrow(() -> new AccessDeniedException("만료되거나 발급되지 않은 토큰입니다.")); } + @Transactional + public String refresh(String refreshToken) { + Token_ foundToken = this.findByToken(refreshToken); + Member member = memberService.findById(foundToken.getMemberId()); + String newAccessToken = createAccessToken(member); + return newAccessToken; + } + } From 5d1152406d4a6516bf8c236adde319c8fbb26f63 Mon Sep 17 00:00:00 2001 From: SJ70 Date: Thu, 25 Jul 2024 21:12:47 +0900 Subject: [PATCH 06/11] =?UTF-8?q?refactor:=20=EA=B8=B0=EC=A1=B4=EC=9D=98?= =?UTF-8?q?=20TokenService=EB=A5=BC=20=EB=8C=80=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/AuthController.java | 1 - .../j9/bestmoments/domain/AccessToken.java | 2 +- .../j9/bestmoments/domain/RefreshToken.java | 2 +- .../java/com/j9/bestmoments/domain/Token.java | 37 +++------- .../com/j9/bestmoments/domain/Token_.java | 23 ------ .../jwt/JwtAuthenticationFilter.java | 2 +- .../repository/TokenRepository.java | 12 ---- .../repository/Token_Repository.java | 4 +- .../j9/bestmoments/service/TokenService.java | 72 ++++++++----------- .../j9/bestmoments/service/Token_Service.java | 67 ----------------- 10 files changed, 44 insertions(+), 178 deletions(-) delete mode 100644 src/main/java/com/j9/bestmoments/domain/Token_.java delete mode 100644 src/main/java/com/j9/bestmoments/repository/TokenRepository.java delete mode 100644 src/main/java/com/j9/bestmoments/service/Token_Service.java diff --git a/src/main/java/com/j9/bestmoments/controller/AuthController.java b/src/main/java/com/j9/bestmoments/controller/AuthController.java index f57957a..6a8942d 100644 --- a/src/main/java/com/j9/bestmoments/controller/AuthController.java +++ b/src/main/java/com/j9/bestmoments/controller/AuthController.java @@ -7,7 +7,6 @@ import com.j9.bestmoments.domain.Member; import com.j9.bestmoments.service.MemberService; import com.j9.bestmoments.service.TokenService; -import com.j9.bestmoments.service.Token_Service; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/j9/bestmoments/domain/AccessToken.java b/src/main/java/com/j9/bestmoments/domain/AccessToken.java index 2494067..367775c 100644 --- a/src/main/java/com/j9/bestmoments/domain/AccessToken.java +++ b/src/main/java/com/j9/bestmoments/domain/AccessToken.java @@ -4,7 +4,7 @@ import org.springframework.data.redis.core.RedisHash; @RedisHash(value = "AccessToken", timeToLive = TokenExpiration.ACCESS_TOKEN) -public class AccessToken extends Token_ { +public class AccessToken extends Token { public AccessToken(Member member, String token) { super(member, token); diff --git a/src/main/java/com/j9/bestmoments/domain/RefreshToken.java b/src/main/java/com/j9/bestmoments/domain/RefreshToken.java index 015ee1f..1987105 100644 --- a/src/main/java/com/j9/bestmoments/domain/RefreshToken.java +++ b/src/main/java/com/j9/bestmoments/domain/RefreshToken.java @@ -4,7 +4,7 @@ import org.springframework.data.redis.core.RedisHash; @RedisHash(value = "RefreshToken", timeToLive = TokenExpiration.REFRESH_TOKEN) -public class RefreshToken extends Token_ { +public class RefreshToken extends Token { public RefreshToken(Member member, String token) { super(member, token); diff --git a/src/main/java/com/j9/bestmoments/domain/Token.java b/src/main/java/com/j9/bestmoments/domain/Token.java index 715c01c..ae6ad1c 100644 --- a/src/main/java/com/j9/bestmoments/domain/Token.java +++ b/src/main/java/com/j9/bestmoments/domain/Token.java @@ -1,40 +1,23 @@ package com.j9.bestmoments.domain; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import lombok.Builder; -import lombok.Getter; import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; +import java.util.UUID; +import lombok.Getter; +import org.springframework.data.redis.core.RedisHash; -@Entity @Getter @NoArgsConstructor +@RedisHash("Token") public class Token { @Id - @GeneratedValue(strategy = GenerationType.AUTO) - private Long id; - - @ManyToOne - @JoinColumn(name = "member_id", referencedColumnName = "id") - private Member member; - - private String refreshToken; - private String accessToken; - - @Builder - public Token(Member member, String refreshToken, String accessToken) { - this.member = member; - this.refreshToken = refreshToken; - this.accessToken = accessToken; - } + private String token; + private UUID memberId; - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; + public Token(Member member, String token) { + this.token = token; + this.memberId = member.getId(); } } diff --git a/src/main/java/com/j9/bestmoments/domain/Token_.java b/src/main/java/com/j9/bestmoments/domain/Token_.java deleted file mode 100644 index ccdd392..0000000 --- a/src/main/java/com/j9/bestmoments/domain/Token_.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.j9.bestmoments.domain; - -import lombok.NoArgsConstructor; -import org.springframework.data.annotation.Id; -import java.util.UUID; -import lombok.Getter; -import org.springframework.data.redis.core.RedisHash; - -@Getter -@NoArgsConstructor -@RedisHash("Token") -public class Token_ { - - @Id - private String token; - private UUID memberId; - - public Token_(Member member, String token) { - this.token = token; - this.memberId = member.getId(); - } - -} diff --git a/src/main/java/com/j9/bestmoments/jwt/JwtAuthenticationFilter.java b/src/main/java/com/j9/bestmoments/jwt/JwtAuthenticationFilter.java index 96f4367..ed5ce39 100644 --- a/src/main/java/com/j9/bestmoments/jwt/JwtAuthenticationFilter.java +++ b/src/main/java/com/j9/bestmoments/jwt/JwtAuthenticationFilter.java @@ -28,7 +28,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha if (token != null && jwtTokenProvider.validateToken(token)) { Authentication authentication = jwtTokenProvider.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(authentication); - tokenService.checkExpired(token); + tokenService.findByToken(token); } chain.doFilter(request, response); diff --git a/src/main/java/com/j9/bestmoments/repository/TokenRepository.java b/src/main/java/com/j9/bestmoments/repository/TokenRepository.java deleted file mode 100644 index 0547c0a..0000000 --- a/src/main/java/com/j9/bestmoments/repository/TokenRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.j9.bestmoments.repository; - -import com.j9.bestmoments.domain.Token; -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface TokenRepository extends JpaRepository { - - Optional findByAccessToken(String accessToken); - Optional findByRefreshToken(String refreshToken); - -} diff --git a/src/main/java/com/j9/bestmoments/repository/Token_Repository.java b/src/main/java/com/j9/bestmoments/repository/Token_Repository.java index ff98f58..20cb2fa 100644 --- a/src/main/java/com/j9/bestmoments/repository/Token_Repository.java +++ b/src/main/java/com/j9/bestmoments/repository/Token_Repository.java @@ -1,11 +1,11 @@ package com.j9.bestmoments.repository; -import com.j9.bestmoments.domain.Token_; +import com.j9.bestmoments.domain.Token; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @Repository -public interface Token_Repository extends CrudRepository { +public interface Token_Repository extends CrudRepository { boolean existsByToken(String token); diff --git a/src/main/java/com/j9/bestmoments/service/TokenService.java b/src/main/java/com/j9/bestmoments/service/TokenService.java index dacbfaf..ccc5509 100644 --- a/src/main/java/com/j9/bestmoments/service/TokenService.java +++ b/src/main/java/com/j9/bestmoments/service/TokenService.java @@ -1,10 +1,12 @@ package com.j9.bestmoments.service; +import com.j9.bestmoments.domain.AccessToken; import com.j9.bestmoments.domain.Member; +import com.j9.bestmoments.domain.RefreshToken; import com.j9.bestmoments.domain.Token; import com.j9.bestmoments.dto.response.LoginDto; import com.j9.bestmoments.jwt.JwtTokenProvider; -import com.j9.bestmoments.repository.TokenRepository; +import com.j9.bestmoments.repository.Token_Repository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.AccessDeniedException; @@ -12,69 +14,53 @@ import org.springframework.transaction.annotation.Transactional; @Service -@Slf4j @RequiredArgsConstructor +@Slf4j public class TokenService { - private final TokenRepository tokenRepository; + private final Token_Repository tokenRepository; private final JwtTokenProvider jwtTokenProvider; + private final MemberService memberService; @Transactional public LoginDto create(Member member) { - String accessToken = jwtTokenProvider.generateAccessToken(member); - String refreshToken = jwtTokenProvider.generateRefreshToken(member); - Token token = Token.builder() - .member(member) - .accessToken(accessToken) - .refreshToken(refreshToken) - .build(); - tokenRepository.save(token); - log.error(token.getAccessToken()); - log.error(token.getRefreshToken()); - log.error(tokenRepository.findAll().get(0).getAccessToken()); + String accessToken = createAccessToken(member); + String refreshToken = createRefreshToken(member); + log.info("토큰 발급됨\n - accessToken: {}\n - refreshToken: {}", accessToken, refreshToken); return new LoginDto("Bearer", accessToken, refreshToken, member.getDeletedAt()); } - public Token findByAnyToken(String token) { - return tokenRepository.findByAccessToken(resolveToken(token)) - .or(() -> tokenRepository.findByRefreshToken(resolveToken(token))) - .orElseThrow(() -> new AccessDeniedException("존재하지 않거나 만료된 토큰입니다.")); - } - - public Token findByAccessToken(String accessToken) { - return tokenRepository.findByAccessToken(resolveToken(accessToken)) - .orElseThrow(() -> new AccessDeniedException("존재하지 않거나 만료된 액세스 토큰입니다.")); - } - - public Token findByRefreshToken(String refreshToken) { - return tokenRepository.findByRefreshToken(resolveToken(refreshToken)) - .orElseThrow(() -> new AccessDeniedException("존재하지 않거나 만료된 리프래시 토큰입니다.")); + private String createAccessToken(Member member) { + String accessTokenValue = jwtTokenProvider.generateAccessToken(member); + AccessToken accessToken = new AccessToken(member, accessTokenValue); + tokenRepository.save(accessToken); + return accessTokenValue; } - public void checkExpired(String token) { - findByAnyToken(token); + private String createRefreshToken(Member member) { + String refreshTokenValue = jwtTokenProvider.generateRefreshToken(member); + RefreshToken refreshToken = new RefreshToken(member, refreshTokenValue); + tokenRepository.save(refreshToken); + return refreshTokenValue; } @Transactional public void expire(String token) { - Token foundToken = findByAnyToken(token); + Token foundToken = this.findByToken(token); tokenRepository.delete(foundToken); } - @Transactional - public String refresh(String refreshToken) { - Token foundToken = findByRefreshToken(refreshToken); - String accessToken = jwtTokenProvider.generateAccessToken(foundToken.getMember()); - foundToken.setAccessToken(accessToken); - tokenRepository.save(foundToken); - return accessToken; + public Token findByToken(String token) { + return tokenRepository.findById(token) + .orElseThrow(() -> new AccessDeniedException("만료되거나 발급되지 않은 토큰입니다.")); } - private String resolveToken(String token) { - if (token.startsWith("Bearer")) { - return token.substring(7); - } - return token; + @Transactional + public String refresh(String refreshToken) { + Token foundToken = this.findByToken(refreshToken); + Member member = memberService.findById(foundToken.getMemberId()); + String newAccessToken = createAccessToken(member); + return newAccessToken; } } diff --git a/src/main/java/com/j9/bestmoments/service/Token_Service.java b/src/main/java/com/j9/bestmoments/service/Token_Service.java deleted file mode 100644 index 41e998a..0000000 --- a/src/main/java/com/j9/bestmoments/service/Token_Service.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.j9.bestmoments.service; - -import com.j9.bestmoments.domain.AccessToken; -import com.j9.bestmoments.domain.Member; -import com.j9.bestmoments.domain.RefreshToken; -import com.j9.bestmoments.domain.Token_; -import com.j9.bestmoments.dto.response.LoginDto; -import com.j9.bestmoments.jwt.JwtTokenProvider; -import com.j9.bestmoments.repository.Token_Repository; -import jakarta.persistence.EntityNotFoundException; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -@Slf4j -public class Token_Service { - - private final Token_Repository tokenRepository; - private final JwtTokenProvider jwtTokenProvider; - private final MemberService memberService; - - @Transactional - public LoginDto create(Member member) { - String accessToken = createAccessToken(member); - String refreshToken = createRefreshToken(member); - log.info("토큰 발급됨\n - accessToken: {}\n - refreshToken: {}", accessToken, refreshToken); - return new LoginDto("Bearer", accessToken, refreshToken, member.getDeletedAt()); - } - - private String createAccessToken(Member member) { - String accessTokenValue = jwtTokenProvider.generateAccessToken(member); - AccessToken accessToken = new AccessToken(member, accessTokenValue); - tokenRepository.save(accessToken); - return accessTokenValue; - } - - private String createRefreshToken(Member member) { - String refreshTokenValue = jwtTokenProvider.generateRefreshToken(member); - RefreshToken refreshToken = new RefreshToken(member, refreshTokenValue); - tokenRepository.save(refreshToken); - return refreshTokenValue; - } - - @Transactional - public void expire(String token) { - Token_ foundToken = this.findByToken(token); - tokenRepository.delete(foundToken); - } - - private Token_ findByToken(String token) { - return tokenRepository.findById(token) - .orElseThrow(() -> new AccessDeniedException("만료되거나 발급되지 않은 토큰입니다.")); - } - - @Transactional - public String refresh(String refreshToken) { - Token_ foundToken = this.findByToken(refreshToken); - Member member = memberService.findById(foundToken.getMemberId()); - String newAccessToken = createAccessToken(member); - return newAccessToken; - } - -} From 2174989bac1fa1f1665211233cc5352a1fa3f4e1 Mon Sep 17 00:00:00 2001 From: SJ70 Date: Thu, 25 Jul 2024 22:10:53 +0900 Subject: [PATCH 07/11] =?UTF-8?q?refactor:=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 액세스 토큰 : 만료 정보를 제외 - 리프레쉬 토큰 : 랜덤 UUID를 반환 --- .../j9/bestmoments/jwt/JwtTokenProvider.java | 25 +++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/j9/bestmoments/jwt/JwtTokenProvider.java b/src/main/java/com/j9/bestmoments/jwt/JwtTokenProvider.java index 056d986..bf0ae72 100644 --- a/src/main/java/com/j9/bestmoments/jwt/JwtTokenProvider.java +++ b/src/main/java/com/j9/bestmoments/jwt/JwtTokenProvider.java @@ -12,9 +12,10 @@ import io.jsonwebtoken.security.Keys; import java.security.Key; import java.util.Collections; -import java.util.Date; +import java.util.UUID; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -26,12 +27,6 @@ public class JwtTokenProvider { private final Key key; - @Value("${jwt.accessTokenExpirationMs}") - private long accessTokenExpirationMs; - - @Value("${jwt.refreshTokenExpirationMs}") - private long refreshTokenExpirationMs; - // secret 값을 암호화 (SHA 키 생성) public JwtTokenProvider(@Value("${jwt.secret}") String secretKey) { byte[] keyBytes = Decoders.BASE64.decode(secretKey); @@ -39,27 +34,15 @@ public JwtTokenProvider(@Value("${jwt.secret}") String secretKey) { } public String generateAccessToken(Member member) { - Date now = new Date(); - Date accessTokenExpiresIn = new Date(now.getTime() + accessTokenExpirationMs); return Jwts.builder() .claim("id", member.getId()) .claim("role", member.getRole().getValue()) - .setIssuedAt(now) - .setExpiration(accessTokenExpiresIn) .signWith(key, SignatureAlgorithm.HS256) .compact(); } public String generateRefreshToken(Member member) { - Date now = new Date(); - Date refreshTokenExpiresIn = new Date(now.getTime() + refreshTokenExpirationMs); - return Jwts.builder() - .claim("id", member.getId()) - .claim("role", member.getRole().getValue()) - .setIssuedAt(now) - .setExpiration(refreshTokenExpiresIn) - .signWith(key, SignatureAlgorithm.HS256) - .compact(); + return UUID.randomUUID().toString(); } // 토큰을 복호화하여 인증 정보 추출 @@ -71,7 +54,7 @@ public Authentication getAuthentication(String accessToken) { .getBody(); if (claims.get("id") == null || claims.get("role") == null) { - throw new RuntimeException("권한 정보가 없는 토큰입니다."); + throw new AccessDeniedException("권한 정보가 없는 토큰입니다."); } String id = claims.get("id").toString(); From 4659719b7d62bb7aaf5749ae1a968f8f588ae44c Mon Sep 17 00:00:00 2001 From: SJ70 Date: Thu, 25 Jul 2024 22:34:44 +0900 Subject: [PATCH 08/11] =?UTF-8?q?refactor:=20LoginDto=EC=97=90=EC=84=9C=20?= =?UTF-8?q?grantType=20=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/j9/bestmoments/dto/response/LoginDto.java | 1 - src/main/java/com/j9/bestmoments/service/TokenService.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/j9/bestmoments/dto/response/LoginDto.java b/src/main/java/com/j9/bestmoments/dto/response/LoginDto.java index d05c54d..7f852cb 100644 --- a/src/main/java/com/j9/bestmoments/dto/response/LoginDto.java +++ b/src/main/java/com/j9/bestmoments/dto/response/LoginDto.java @@ -3,7 +3,6 @@ import java.time.LocalDateTime; public record LoginDto( - String grantType, String accessToken, String refreshToken, LocalDateTime deletedAt diff --git a/src/main/java/com/j9/bestmoments/service/TokenService.java b/src/main/java/com/j9/bestmoments/service/TokenService.java index ccc5509..ce6ad87 100644 --- a/src/main/java/com/j9/bestmoments/service/TokenService.java +++ b/src/main/java/com/j9/bestmoments/service/TokenService.java @@ -27,7 +27,7 @@ public LoginDto create(Member member) { String accessToken = createAccessToken(member); String refreshToken = createRefreshToken(member); log.info("토큰 발급됨\n - accessToken: {}\n - refreshToken: {}", accessToken, refreshToken); - return new LoginDto("Bearer", accessToken, refreshToken, member.getDeletedAt()); + return new LoginDto(accessToken, refreshToken, member.getDeletedAt()); } private String createAccessToken(Member member) { From c71cfd6cd161181e2f2674829539f1cc3540ec1e Mon Sep 17 00:00:00 2001 From: SJ70 Date: Thu, 25 Jul 2024 23:07:41 +0900 Subject: [PATCH 09/11] =?UTF-8?q?rename:=20TokenRepository=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=A0=95=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{Token_Repository.java => TokenRepository.java} | 2 +- src/main/java/com/j9/bestmoments/service/TokenService.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/main/java/com/j9/bestmoments/repository/{Token_Repository.java => TokenRepository.java} (76%) diff --git a/src/main/java/com/j9/bestmoments/repository/Token_Repository.java b/src/main/java/com/j9/bestmoments/repository/TokenRepository.java similarity index 76% rename from src/main/java/com/j9/bestmoments/repository/Token_Repository.java rename to src/main/java/com/j9/bestmoments/repository/TokenRepository.java index 20cb2fa..209103a 100644 --- a/src/main/java/com/j9/bestmoments/repository/Token_Repository.java +++ b/src/main/java/com/j9/bestmoments/repository/TokenRepository.java @@ -5,7 +5,7 @@ import org.springframework.stereotype.Repository; @Repository -public interface Token_Repository extends CrudRepository { +public interface TokenRepository extends CrudRepository { boolean existsByToken(String token); diff --git a/src/main/java/com/j9/bestmoments/service/TokenService.java b/src/main/java/com/j9/bestmoments/service/TokenService.java index ce6ad87..cab067d 100644 --- a/src/main/java/com/j9/bestmoments/service/TokenService.java +++ b/src/main/java/com/j9/bestmoments/service/TokenService.java @@ -6,7 +6,7 @@ import com.j9.bestmoments.domain.Token; import com.j9.bestmoments.dto.response.LoginDto; import com.j9.bestmoments.jwt.JwtTokenProvider; -import com.j9.bestmoments.repository.Token_Repository; +import com.j9.bestmoments.repository.TokenRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.AccessDeniedException; @@ -18,7 +18,7 @@ @Slf4j public class TokenService { - private final Token_Repository tokenRepository; + private final TokenRepository tokenRepository; private final JwtTokenProvider jwtTokenProvider; private final MemberService memberService; From 1104b7ee32c0ddcb7941318813ab0306864f4089 Mon Sep 17 00:00:00 2001 From: SJ70 Date: Fri, 26 Jul 2024 01:01:04 +0900 Subject: [PATCH 10/11] =?UTF-8?q?refactor:=20=EC=95=A1=EC=84=B8=EC=8A=A4?= =?UTF-8?q?=ED=86=A0=ED=81=B0=EA=B3=BC=20=EB=A6=AC=ED=94=84=EB=A0=88?= =?UTF-8?q?=EC=89=AC=ED=86=A0=ED=81=B0=EC=9D=84=20Enum=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EA=B5=AC=EB=B6=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존의 RefreshToken, AccessToken 객체를 제거 --- .../j9/bestmoments/domain/AccessToken.java | 13 ---------- .../j9/bestmoments/domain/RefreshToken.java | 13 ---------- .../java/com/j9/bestmoments/domain/Token.java | 20 ++++++++++++---- .../com/j9/bestmoments/domain/TokenType.java | 18 ++++++++++++++ .../repository/TokenRepository.java | 2 -- .../j9/bestmoments/service/TokenService.java | 24 ++++++++++++------- 6 files changed, 50 insertions(+), 40 deletions(-) delete mode 100644 src/main/java/com/j9/bestmoments/domain/AccessToken.java delete mode 100644 src/main/java/com/j9/bestmoments/domain/RefreshToken.java create mode 100644 src/main/java/com/j9/bestmoments/domain/TokenType.java diff --git a/src/main/java/com/j9/bestmoments/domain/AccessToken.java b/src/main/java/com/j9/bestmoments/domain/AccessToken.java deleted file mode 100644 index 367775c..0000000 --- a/src/main/java/com/j9/bestmoments/domain/AccessToken.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.j9.bestmoments.domain; - -import com.j9.bestmoments.constants.TokenExpiration; -import org.springframework.data.redis.core.RedisHash; - -@RedisHash(value = "AccessToken", timeToLive = TokenExpiration.ACCESS_TOKEN) -public class AccessToken extends Token { - - public AccessToken(Member member, String token) { - super(member, token); - } - -} diff --git a/src/main/java/com/j9/bestmoments/domain/RefreshToken.java b/src/main/java/com/j9/bestmoments/domain/RefreshToken.java deleted file mode 100644 index 1987105..0000000 --- a/src/main/java/com/j9/bestmoments/domain/RefreshToken.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.j9.bestmoments.domain; - -import com.j9.bestmoments.constants.TokenExpiration; -import org.springframework.data.redis.core.RedisHash; - -@RedisHash(value = "RefreshToken", timeToLive = TokenExpiration.REFRESH_TOKEN) -public class RefreshToken extends Token { - - public RefreshToken(Member member, String token) { - super(member, token); - } - -} diff --git a/src/main/java/com/j9/bestmoments/domain/Token.java b/src/main/java/com/j9/bestmoments/domain/Token.java index ae6ad1c..dab7e19 100644 --- a/src/main/java/com/j9/bestmoments/domain/Token.java +++ b/src/main/java/com/j9/bestmoments/domain/Token.java @@ -1,23 +1,35 @@ package com.j9.bestmoments.domain; +import lombok.Builder; import lombok.NoArgsConstructor; import org.springframework.data.annotation.Id; import java.util.UUID; import lombok.Getter; import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.TimeToLive; @Getter @NoArgsConstructor -@RedisHash("Token") -public class Token { +@RedisHash(value = "Token") +public +class Token { @Id private String token; + + private TokenType tokenType; + + @TimeToLive + private Long expiration; + private UUID memberId; - public Token(Member member, String token) { - this.token = token; + @Builder + public Token(Member member, TokenType tokenType, String token) { this.memberId = member.getId(); + this.token = token; + this.tokenType = tokenType; + this.expiration = tokenType.getExpiration(); } } diff --git a/src/main/java/com/j9/bestmoments/domain/TokenType.java b/src/main/java/com/j9/bestmoments/domain/TokenType.java new file mode 100644 index 0000000..d069d47 --- /dev/null +++ b/src/main/java/com/j9/bestmoments/domain/TokenType.java @@ -0,0 +1,18 @@ +package com.j9.bestmoments.domain; + +import com.j9.bestmoments.constants.TokenExpiration; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum TokenType { + + ACCESS_TOKEN("accessToken", TokenExpiration.ACCESS_TOKEN), + REFRESH_TOKEN("refreshToken", TokenExpiration.REFRESH_TOKEN), + ; + + private final String name; + private final long expiration; + +} diff --git a/src/main/java/com/j9/bestmoments/repository/TokenRepository.java b/src/main/java/com/j9/bestmoments/repository/TokenRepository.java index 209103a..84d2e64 100644 --- a/src/main/java/com/j9/bestmoments/repository/TokenRepository.java +++ b/src/main/java/com/j9/bestmoments/repository/TokenRepository.java @@ -7,6 +7,4 @@ @Repository public interface TokenRepository extends CrudRepository { - boolean existsByToken(String token); - } diff --git a/src/main/java/com/j9/bestmoments/service/TokenService.java b/src/main/java/com/j9/bestmoments/service/TokenService.java index cab067d..9404041 100644 --- a/src/main/java/com/j9/bestmoments/service/TokenService.java +++ b/src/main/java/com/j9/bestmoments/service/TokenService.java @@ -1,9 +1,8 @@ package com.j9.bestmoments.service; -import com.j9.bestmoments.domain.AccessToken; import com.j9.bestmoments.domain.Member; -import com.j9.bestmoments.domain.RefreshToken; import com.j9.bestmoments.domain.Token; +import com.j9.bestmoments.domain.TokenType; import com.j9.bestmoments.dto.response.LoginDto; import com.j9.bestmoments.jwt.JwtTokenProvider; import com.j9.bestmoments.repository.TokenRepository; @@ -27,21 +26,30 @@ public LoginDto create(Member member) { String accessToken = createAccessToken(member); String refreshToken = createRefreshToken(member); log.info("토큰 발급됨\n - accessToken: {}\n - refreshToken: {}", accessToken, refreshToken); + this.findByToken(accessToken); return new LoginDto(accessToken, refreshToken, member.getDeletedAt()); } private String createAccessToken(Member member) { - String accessTokenValue = jwtTokenProvider.generateAccessToken(member); - AccessToken accessToken = new AccessToken(member, accessTokenValue); + String token = jwtTokenProvider.generateAccessToken(member); + Token accessToken = Token.builder() + .member(member) + .tokenType(TokenType.ACCESS_TOKEN) + .token(token) + .build(); tokenRepository.save(accessToken); - return accessTokenValue; + return token; } private String createRefreshToken(Member member) { - String refreshTokenValue = jwtTokenProvider.generateRefreshToken(member); - RefreshToken refreshToken = new RefreshToken(member, refreshTokenValue); + String token = jwtTokenProvider.generateRefreshToken(member); + Token refreshToken = Token.builder() + .member(member) + .tokenType(TokenType.REFRESH_TOKEN) + .token(token) + .build(); tokenRepository.save(refreshToken); - return refreshTokenValue; + return token; } @Transactional From c9246fc19464f5ad1023827b16c7c3dccf2b0bdd Mon Sep 17 00:00:00 2001 From: SJ70 Date: Fri, 26 Jul 2024 01:04:21 +0900 Subject: [PATCH 11/11] =?UTF-8?q?fix:=20=ED=86=A0=ED=81=B0=20=EC=9E=AC?= =?UTF-8?q?=EB=B0=9C=EA=B8=89=EC=9D=80=20refreshToken=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=EB=A7=8C=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/j9/bestmoments/service/TokenService.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/j9/bestmoments/service/TokenService.java b/src/main/java/com/j9/bestmoments/service/TokenService.java index 9404041..074757e 100644 --- a/src/main/java/com/j9/bestmoments/service/TokenService.java +++ b/src/main/java/com/j9/bestmoments/service/TokenService.java @@ -66,6 +66,9 @@ public Token findByToken(String token) { @Transactional public String refresh(String refreshToken) { Token foundToken = this.findByToken(refreshToken); + if (!foundToken.getTokenType().equals(TokenType.REFRESH_TOKEN)) { + throw new AccessDeniedException("만료되거나 발급되지 않은 토큰입니다."); + } Member member = memberService.findById(foundToken.getMemberId()); String newAccessToken = createAccessToken(member); return newAccessToken;