From 85e4abcff913c071aee3945008c2ed1ad4d4b0bd Mon Sep 17 00:00:00 2001 From: Jin Geonwoo Date: Sun, 2 Jun 2024 20:57:42 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EB=A6=AC=ED=94=84=EB=A0=88?= =?UTF-8?q?=EC=89=AC=20=ED=86=A0=ED=81=B0=20=EC=83=9D=EC=84=B1=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/sopt/common/auth/dto/AuthToken.java | 7 +++++++ .../common/auth/jwt/JwtTokenProvider.java | 11 +++++++++-- .../dto/response/MemberJoinResponse.java | 7 +++++-- .../java/org/sopt/service/MemberService.java | 19 +++++-------------- 4 files changed, 26 insertions(+), 18 deletions(-) create mode 100644 sixthAssignment/src/main/java/org/sopt/common/auth/dto/AuthToken.java diff --git a/sixthAssignment/src/main/java/org/sopt/common/auth/dto/AuthToken.java b/sixthAssignment/src/main/java/org/sopt/common/auth/dto/AuthToken.java new file mode 100644 index 0000000..2cf5a5f --- /dev/null +++ b/sixthAssignment/src/main/java/org/sopt/common/auth/dto/AuthToken.java @@ -0,0 +1,7 @@ +package org.sopt.common.auth.dto; + +public record AuthToken( + String accessToken, + String refreshToken +) { +} diff --git a/sixthAssignment/src/main/java/org/sopt/common/auth/jwt/JwtTokenProvider.java b/sixthAssignment/src/main/java/org/sopt/common/auth/jwt/JwtTokenProvider.java index cff8a86..72028f9 100644 --- a/sixthAssignment/src/main/java/org/sopt/common/auth/jwt/JwtTokenProvider.java +++ b/sixthAssignment/src/main/java/org/sopt/common/auth/jwt/JwtTokenProvider.java @@ -3,6 +3,7 @@ import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; import lombok.RequiredArgsConstructor; +import org.sopt.common.auth.dto.AuthToken; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; @@ -23,12 +24,18 @@ public class JwtTokenProvider { @Value("${jwt.secret}") private String JWT_SECRET; + public AuthToken issueToken(final Authentication authentication) { + return new AuthToken( + issueAccessToken(authentication), + issueRefreshToken(authentication) + ); + } - public String issueAccessToken(final Authentication authentication) { + private String issueAccessToken(final Authentication authentication) { return generateToken(authentication, ACCESS_TOKEN_EXPIRATION_TIME); } - public String issueRefreshToken(final Authentication authentication) { + private String issueRefreshToken(final Authentication authentication) { return generateToken(authentication, ACCESS_TOKEN_EXPIRATION_TIME); } diff --git a/sixthAssignment/src/main/java/org/sopt/common/dto/response/MemberJoinResponse.java b/sixthAssignment/src/main/java/org/sopt/common/dto/response/MemberJoinResponse.java index 787c9e3..d9c8961 100644 --- a/sixthAssignment/src/main/java/org/sopt/common/dto/response/MemberJoinResponse.java +++ b/sixthAssignment/src/main/java/org/sopt/common/dto/response/MemberJoinResponse.java @@ -1,14 +1,17 @@ package org.sopt.common.dto.response; +import org.sopt.common.auth.dto.AuthToken; + public record MemberJoinResponse( String accessToken, + String refreshToken, String userId ) { public static MemberJoinResponse of( - String accessToken, + AuthToken token, String userId ) { - return new MemberJoinResponse(accessToken, userId); + return new MemberJoinResponse(token.accessToken(), token.refreshToken(), userId); } } diff --git a/sixthAssignment/src/main/java/org/sopt/service/MemberService.java b/sixthAssignment/src/main/java/org/sopt/service/MemberService.java index 52fe568..7c4bcfc 100644 --- a/sixthAssignment/src/main/java/org/sopt/service/MemberService.java +++ b/sixthAssignment/src/main/java/org/sopt/service/MemberService.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.sopt.common.auth.UserAuthentication; +import org.sopt.common.auth.dto.AuthToken; import org.sopt.common.dto.request.MemberJoinRequest; import org.sopt.common.dto.response.MemberJoinResponse; import org.sopt.common.auth.jwt.JwtTokenProvider; @@ -24,30 +25,20 @@ public class MemberService { private final MemberRepository memberRepository; private final JwtTokenProvider jwtTokenProvider; - @Transactional - public String createMemberWithoutAuth( - MemberCreateRequest request - ) { - Member member = request.toEntity(); - memberRepository.save(member); - - return member.getId().toString(); - } - @Transactional public MemberJoinResponse createMember( MemberJoinRequest request ) { Member member = memberRepository.save(request.toEntity()); - String accessToken = getToken(member.getId()); + AuthToken token = getToken(member.getId()); - return MemberJoinResponse.of(accessToken, member.getId().toString()); + return MemberJoinResponse.of(token, member.getId().toString()); } - private String getToken(Long memberId) { + private AuthToken getToken(Long memberId) { UserAuthentication authentication = UserAuthentication.createUserAuthentication(memberId); - return jwtTokenProvider.issueAccessToken(authentication); + return jwtTokenProvider.issueToken(authentication); } public MemberGetResponse findMemberById( From c40028b6484dbf77f618c71b5e69ce34fe045ef3 Mon Sep 17 00:00:00 2001 From: Jin Geonwoo Date: Mon, 3 Jun 2024 15:33:41 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EB=A6=AC=ED=94=84=EB=A0=88?= =?UTF-8?q?=EC=89=AC=20=ED=86=A0=ED=81=B0=20=EC=A0=80=EC=9E=A5=20=EB=B0=8F?= =?UTF-8?q?=20=EC=95=A1=EC=84=B8=EC=8A=A4=20=ED=86=A0=ED=81=B0=20=EC=9E=AC?= =?UTF-8?q?=EB=B0=9C=EA=B8=89=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sixthAssignment/build.gradle | 3 +- .../common/auth/JwtAuthenticationFilter.java | 2 +- .../org/sopt/common/auth/dto/AuthToken.java | 7 --- .../sopt/common/auth/dto/AuthTokenSet.java | 16 +++++++ .../common/auth/jwt/JwtTokenProvider.java | 45 ++++++++++--------- .../sopt/common/auth/redis/domain/Token.java | 21 +++++++++ .../redis/repository/TokenRepository.java | 13 ++++++ .../auth/redis/service/TokenService.java | 22 +++++++++ .../dto/response/MemberJoinResponse.java | 4 +- .../response/MemberTokenRefreshResponse.java | 10 +++++ .../exception/message/ErrorMessage.java | 1 + .../java/org/sopt/config/RedisConfig.java | 32 +++++++++++++ .../org/sopt/controller/MemberController.java | 14 +++++- .../java/org/sopt/service/MemberService.java | 36 +++++++++------ 14 files changed, 181 insertions(+), 45 deletions(-) delete mode 100644 sixthAssignment/src/main/java/org/sopt/common/auth/dto/AuthToken.java create mode 100644 sixthAssignment/src/main/java/org/sopt/common/auth/dto/AuthTokenSet.java create mode 100644 sixthAssignment/src/main/java/org/sopt/common/auth/redis/domain/Token.java create mode 100644 sixthAssignment/src/main/java/org/sopt/common/auth/redis/repository/TokenRepository.java create mode 100644 sixthAssignment/src/main/java/org/sopt/common/auth/redis/service/TokenService.java create mode 100644 sixthAssignment/src/main/java/org/sopt/common/dto/response/MemberTokenRefreshResponse.java create mode 100644 sixthAssignment/src/main/java/org/sopt/config/RedisConfig.java diff --git a/sixthAssignment/build.gradle b/sixthAssignment/build.gradle index c851a6e..73e0bf8 100644 --- a/sixthAssignment/build.gradle +++ b/sixthAssignment/build.gradle @@ -27,7 +27,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.mysql:mysql-connector-j' - annotationProcessor 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' //JWT @@ -37,6 +37,7 @@ dependencies { //Security implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' //Multipart file implementation("software.amazon.awssdk:bom:2.21.0") diff --git a/sixthAssignment/src/main/java/org/sopt/common/auth/JwtAuthenticationFilter.java b/sixthAssignment/src/main/java/org/sopt/common/auth/JwtAuthenticationFilter.java index 8e33480..e765602 100644 --- a/sixthAssignment/src/main/java/org/sopt/common/auth/JwtAuthenticationFilter.java +++ b/sixthAssignment/src/main/java/org/sopt/common/auth/JwtAuthenticationFilter.java @@ -35,7 +35,7 @@ protected void doFilterInternal( final String token = getJwtFromRequest(request); if (jwtTokenProvider.validateToken(token) == VALID_JWT) { - Long memberId = jwtTokenProvider.getUserFromJwt(token); + Long memberId = jwtTokenProvider.getMemberIdFromToken(token); setAuthentication(request, memberId); } } catch (Exception exception) { diff --git a/sixthAssignment/src/main/java/org/sopt/common/auth/dto/AuthToken.java b/sixthAssignment/src/main/java/org/sopt/common/auth/dto/AuthToken.java deleted file mode 100644 index 2cf5a5f..0000000 --- a/sixthAssignment/src/main/java/org/sopt/common/auth/dto/AuthToken.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.sopt.common.auth.dto; - -public record AuthToken( - String accessToken, - String refreshToken -) { -} diff --git a/sixthAssignment/src/main/java/org/sopt/common/auth/dto/AuthTokenSet.java b/sixthAssignment/src/main/java/org/sopt/common/auth/dto/AuthTokenSet.java new file mode 100644 index 0000000..d079be4 --- /dev/null +++ b/sixthAssignment/src/main/java/org/sopt/common/auth/dto/AuthTokenSet.java @@ -0,0 +1,16 @@ +package org.sopt.common.auth.dto; + +public record AuthTokenSet( + String accessToken, + String refreshToken +) { + + public static AuthTokenSet of(String accessToken, String refreshToken) { + return new AuthTokenSet(accessToken, refreshToken); + } + + public enum Type { + ACCESS_TOKEN, + REFRESH_TOKEN + } +} diff --git a/sixthAssignment/src/main/java/org/sopt/common/auth/jwt/JwtTokenProvider.java b/sixthAssignment/src/main/java/org/sopt/common/auth/jwt/JwtTokenProvider.java index 72028f9..0af125d 100644 --- a/sixthAssignment/src/main/java/org/sopt/common/auth/jwt/JwtTokenProvider.java +++ b/sixthAssignment/src/main/java/org/sopt/common/auth/jwt/JwtTokenProvider.java @@ -3,7 +3,9 @@ import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; import lombok.RequiredArgsConstructor; -import org.sopt.common.auth.dto.AuthToken; +import org.sopt.common.auth.UserAuthentication; +import org.sopt.common.auth.dto.AuthTokenSet; +import org.sopt.common.auth.redis.service.TokenService; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; @@ -17,42 +19,45 @@ public class JwtTokenProvider { private static final String USER_ID = "userId"; - private static final Long ACCESS_TOKEN_EXPIRATION_TIME = 24 * 60 * 60 * 1000L * 14; private static final Long REFRESH_TOKEN_EXPIRATION_TIME = 24 * 60 * 60 * 1000L * 14; + private final TokenService tokenService; @Value("${jwt.secret}") private String JWT_SECRET; - public AuthToken issueToken(final Authentication authentication) { - return new AuthToken( - issueAccessToken(authentication), - issueRefreshToken(authentication) - ); - } + public String issueToken(Long memberId, AuthTokenSet.Type type) { + Authentication authentication = UserAuthentication.createUserAuthentication(memberId); - private String issueAccessToken(final Authentication authentication) { - return generateToken(authentication, ACCESS_TOKEN_EXPIRATION_TIME); - } + if (type == AuthTokenSet.Type.ACCESS_TOKEN) + return generateToken(authentication, ACCESS_TOKEN_EXPIRATION_TIME); - private String issueRefreshToken(final Authentication authentication) { - return generateToken(authentication, ACCESS_TOKEN_EXPIRATION_TIME); - } + String refreshToken = generateToken(authentication, REFRESH_TOKEN_EXPIRATION_TIME); + tokenService.save(memberId, refreshToken); + return refreshToken; + } public String generateToken(Authentication authentication, Long tokenExpirationTime) { + Claims claims = generateClaims(authentication, tokenExpirationTime); + + return Jwts.builder() + .setHeaderParam(Header.TYPE, Header.JWT_TYPE) // Header + .setClaims(claims) // Claim + .signWith(getSigningKey()) // Signature + .compact(); + } + + private Claims generateClaims(Authentication authentication, Long tokenExpirationTime) { final Date now = new Date(); + final Claims claims = Jwts.claims() .setIssuedAt(now) .setExpiration(new Date(now.getTime() + tokenExpirationTime)); // 만료 시간 claims.put(USER_ID, authentication.getPrincipal()); - return Jwts.builder() - .setHeaderParam(Header.TYPE, Header.JWT_TYPE) // Header - .setClaims(claims) // Claim - .signWith(getSigningKey()) // Signature - .compact(); + return claims; } private SecretKey getSigningKey() { @@ -84,7 +89,7 @@ private Claims getBody(final String token) { .getBody(); } - public Long getUserFromJwt(String token) { + public Long getMemberIdFromToken(String token) { Claims claims = getBody(token); return Long.valueOf(claims.get(USER_ID).toString()); } diff --git a/sixthAssignment/src/main/java/org/sopt/common/auth/redis/domain/Token.java b/sixthAssignment/src/main/java/org/sopt/common/auth/redis/domain/Token.java new file mode 100644 index 0000000..35fb54f --- /dev/null +++ b/sixthAssignment/src/main/java/org/sopt/common/auth/redis/domain/Token.java @@ -0,0 +1,21 @@ +package org.sopt.common.auth.redis.domain; + +import lombok.AllArgsConstructor; +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; + +@RedisHash(timeToLive = 60 * 60 * 24 * 1000L * 14) +@AllArgsConstructor +@Builder +@Getter +public class Token { + + @Id + private Long memberId; + + @Indexed + private String refreshToken; +} diff --git a/sixthAssignment/src/main/java/org/sopt/common/auth/redis/repository/TokenRepository.java b/sixthAssignment/src/main/java/org/sopt/common/auth/redis/repository/TokenRepository.java new file mode 100644 index 0000000..4b64f7d --- /dev/null +++ b/sixthAssignment/src/main/java/org/sopt/common/auth/redis/repository/TokenRepository.java @@ -0,0 +1,13 @@ +package org.sopt.common.auth.redis.repository; + +import org.sopt.common.auth.redis.domain.Token; +import org.springframework.data.repository.CrudRepository; + +import java.util.Optional; + +public interface TokenRepository extends CrudRepository { + + Optional findByMemberId(Long memberId); + Optional findByMemberIdAndRefreshToken(Long memberId, String refreshToken); + Optional findByRefreshToken(String refreshToken); +} diff --git a/sixthAssignment/src/main/java/org/sopt/common/auth/redis/service/TokenService.java b/sixthAssignment/src/main/java/org/sopt/common/auth/redis/service/TokenService.java new file mode 100644 index 0000000..f9b3253 --- /dev/null +++ b/sixthAssignment/src/main/java/org/sopt/common/auth/redis/service/TokenService.java @@ -0,0 +1,22 @@ +package org.sopt.common.auth.redis.service; + +import lombok.RequiredArgsConstructor; +import org.sopt.common.auth.redis.domain.Token; +import org.sopt.common.auth.redis.repository.TokenRepository; +import org.sopt.common.exception.NotFoundException; +import org.sopt.common.exception.message.ErrorMessage; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class TokenService { + + private final TokenRepository tokenRepository; + + public void save(Long memberId, String refreshToken) { + tokenRepository.save(Token.builder() + .memberId(memberId) + .refreshToken(refreshToken) + .build()); + } +} diff --git a/sixthAssignment/src/main/java/org/sopt/common/dto/response/MemberJoinResponse.java b/sixthAssignment/src/main/java/org/sopt/common/dto/response/MemberJoinResponse.java index d9c8961..3ba9095 100644 --- a/sixthAssignment/src/main/java/org/sopt/common/dto/response/MemberJoinResponse.java +++ b/sixthAssignment/src/main/java/org/sopt/common/dto/response/MemberJoinResponse.java @@ -1,6 +1,6 @@ package org.sopt.common.dto.response; -import org.sopt.common.auth.dto.AuthToken; +import org.sopt.common.auth.dto.AuthTokenSet; public record MemberJoinResponse( String accessToken, @@ -9,7 +9,7 @@ public record MemberJoinResponse( ) { public static MemberJoinResponse of( - AuthToken token, + AuthTokenSet token, String userId ) { return new MemberJoinResponse(token.accessToken(), token.refreshToken(), userId); diff --git a/sixthAssignment/src/main/java/org/sopt/common/dto/response/MemberTokenRefreshResponse.java b/sixthAssignment/src/main/java/org/sopt/common/dto/response/MemberTokenRefreshResponse.java new file mode 100644 index 0000000..7da35c6 --- /dev/null +++ b/sixthAssignment/src/main/java/org/sopt/common/dto/response/MemberTokenRefreshResponse.java @@ -0,0 +1,10 @@ +package org.sopt.common.dto.response; + +public record MemberTokenRefreshResponse( + String accessToken +) { + + public static MemberTokenRefreshResponse of(String accessToken) { + return new MemberTokenRefreshResponse(accessToken); + } +} diff --git a/sixthAssignment/src/main/java/org/sopt/common/exception/message/ErrorMessage.java b/sixthAssignment/src/main/java/org/sopt/common/exception/message/ErrorMessage.java index d4ab444..c79e0f6 100644 --- a/sixthAssignment/src/main/java/org/sopt/common/exception/message/ErrorMessage.java +++ b/sixthAssignment/src/main/java/org/sopt/common/exception/message/ErrorMessage.java @@ -9,6 +9,7 @@ public enum ErrorMessage { MEMBER_NOT_FOUND_BY_ID_EXCEPTION(HttpStatus.NOT_FOUND.value(), "ID에 해당하는 사용자가 존재하지 않습니다."), + MEMBER_NOT_FOUND_BY_REFRESH_TOKEN_EXCEPTION(HttpStatus.NOT_FOUND.value(), "리프레시 토큰에 해당하는 사용자가 존재하지 않습니다."), BLOG_NOT_FOUND_BY_ID_EXCEPTION(HttpStatus.NOT_FOUND.value(), "ID에 해당하는 블로그가 존재하지 않습니다."), JWT_UNAUTHORIZED_EXCEPTION(HttpStatus.UNAUTHORIZED.value(), "사용자의 로그인 검증을 실패했습니다."), ; diff --git a/sixthAssignment/src/main/java/org/sopt/config/RedisConfig.java b/sixthAssignment/src/main/java/org/sopt/config/RedisConfig.java new file mode 100644 index 0000000..cdd04a8 --- /dev/null +++ b/sixthAssignment/src/main/java/org/sopt/config/RedisConfig.java @@ -0,0 +1,32 @@ +package org.sopt.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; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@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); + } + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + return redisTemplate; + } +} diff --git a/sixthAssignment/src/main/java/org/sopt/controller/MemberController.java b/sixthAssignment/src/main/java/org/sopt/controller/MemberController.java index ef04bb3..82c61b9 100644 --- a/sixthAssignment/src/main/java/org/sopt/controller/MemberController.java +++ b/sixthAssignment/src/main/java/org/sopt/controller/MemberController.java @@ -5,11 +5,14 @@ import org.sopt.common.dto.response.MemberGetAllResponse; import org.sopt.common.dto.response.MemberGetResponse; import org.sopt.common.dto.response.MemberJoinResponse; +import org.sopt.common.dto.response.MemberTokenRefreshResponse; import org.sopt.service.MemberService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.security.Principal; + @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/member") @@ -28,11 +31,20 @@ public ResponseEntity postMember( .body(memberJoinResponse); } + @GetMapping("/refresh") + public ResponseEntity refreshAccessToken( + @RequestHeader("Authorization") String refreshToken + ) { + return ResponseEntity.status(HttpStatus.OK) + .body(memberService.refreshAccessToken(refreshToken)); + } + @GetMapping("/{memberId}") public ResponseEntity findMemberById( @PathVariable Long memberId ) { - return ResponseEntity.ok(memberService.findMemberById(memberId)); + return ResponseEntity.status(HttpStatus.OK) + .body(MemberGetResponse.of(memberService.findById(memberId))); } @DeleteMapping("/{memberId}") diff --git a/sixthAssignment/src/main/java/org/sopt/service/MemberService.java b/sixthAssignment/src/main/java/org/sopt/service/MemberService.java index 7c4bcfc..d98717b 100644 --- a/sixthAssignment/src/main/java/org/sopt/service/MemberService.java +++ b/sixthAssignment/src/main/java/org/sopt/service/MemberService.java @@ -1,13 +1,14 @@ package org.sopt.service; import lombok.RequiredArgsConstructor; -import org.sopt.common.auth.UserAuthentication; -import org.sopt.common.auth.dto.AuthToken; +import org.sopt.common.auth.dto.AuthTokenSet; +import org.sopt.common.auth.redis.service.TokenService; import org.sopt.common.dto.request.MemberJoinRequest; import org.sopt.common.dto.response.MemberJoinResponse; import org.sopt.common.auth.jwt.JwtTokenProvider; +import org.sopt.common.dto.response.MemberTokenRefreshResponse; +import org.sopt.common.exception.UnauthorizedException; import org.sopt.domain.Member; -import org.sopt.common.dto.request.MemberCreateRequest; import org.sopt.common.dto.response.MemberGetAllResponse; import org.sopt.common.dto.response.MemberGetResponse; import org.sopt.common.exception.NotFoundException; @@ -16,36 +17,45 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.security.Principal; import java.util.List; +import static org.sopt.common.auth.dto.AuthTokenSet.Type.ACCESS_TOKEN; +import static org.sopt.common.auth.dto.AuthTokenSet.Type.REFRESH_TOKEN; +import static org.sopt.common.auth.jwt.JwtValidationType.VALID_JWT; + @Service @RequiredArgsConstructor public class MemberService { private final MemberRepository memberRepository; private final JwtTokenProvider jwtTokenProvider; + private final TokenService tokenService; @Transactional public MemberJoinResponse createMember( MemberJoinRequest request ) { Member member = memberRepository.save(request.toEntity()); - AuthToken token = getToken(member.getId()); + AuthTokenSet token = getTokenSet(member.getId()); return MemberJoinResponse.of(token, member.getId().toString()); } - private AuthToken getToken(Long memberId) { - UserAuthentication authentication = UserAuthentication.createUserAuthentication(memberId); - - return jwtTokenProvider.issueToken(authentication); + private AuthTokenSet getTokenSet(Long memberId) { + return AuthTokenSet.of( + jwtTokenProvider.issueToken(memberId, ACCESS_TOKEN), + jwtTokenProvider.issueToken(memberId, REFRESH_TOKEN) + ); } - public MemberGetResponse findMemberById( - Long memberId - ) { - return MemberGetResponse.of(memberRepository.findById(memberId) - .orElseThrow(() -> new NotFoundException(ErrorMessage.MEMBER_NOT_FOUND_BY_ID_EXCEPTION))); + public MemberTokenRefreshResponse refreshAccessToken(String refreshToken) { + if (jwtTokenProvider.validateToken(refreshToken) != VALID_JWT) + throw new UnauthorizedException(ErrorMessage.JWT_UNAUTHORIZED_EXCEPTION); + + Long memberId = jwtTokenProvider.getMemberIdFromToken(refreshToken); + String accessToken = jwtTokenProvider.issueToken(memberId, ACCESS_TOKEN); + return MemberTokenRefreshResponse.of(accessToken); } public Member findById(Long memberId) {