diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index b5352cd..c17d6c5 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -2,7 +2,7 @@ name: Backend CD # actions 이름 on: push: - branches: [ develop ] + branches: [ feat/#47 ] jobs: deploy: diff --git a/domain/src/main/java/com/pocket/domain/port/user/LoadUserInfoPort.java b/domain/src/main/java/com/pocket/domain/port/user/LoadUserInfoPort.java index 88ff3c3..153f6ce 100644 --- a/domain/src/main/java/com/pocket/domain/port/user/LoadUserInfoPort.java +++ b/domain/src/main/java/com/pocket/domain/port/user/LoadUserInfoPort.java @@ -1,9 +1,12 @@ package com.pocket.domain.port.user; +import com.pocket.domain.dto.user.LoginResponse; import com.pocket.domain.dto.user.UserInfoDTO; public interface LoadUserInfoPort { UserInfoDTO loadUserInfo(String name); + LoginResponse createToken(String email); + } diff --git a/domain/src/main/java/com/pocket/domain/service/user/UserQueryService.java b/domain/src/main/java/com/pocket/domain/service/user/UserQueryService.java index 02e1ce0..6a9b8d3 100644 --- a/domain/src/main/java/com/pocket/domain/service/user/UserQueryService.java +++ b/domain/src/main/java/com/pocket/domain/service/user/UserQueryService.java @@ -1,5 +1,6 @@ package com.pocket.domain.service.user; +import com.pocket.domain.dto.user.LoginResponse; import com.pocket.domain.dto.user.UserInfoDTO; import com.pocket.domain.port.user.LoadUserInfoPort; import com.pocket.domain.usecase.user.LoginUseCase; @@ -15,4 +16,8 @@ public class UserQueryService implements LoginUseCase { public UserInfoDTO getUserInfo(String name) { return loadUserInfoPort.loadUserInfo(name); } + + public LoginResponse reissueToken(String email) { + return loadUserInfoPort.createToken(email); + } } diff --git a/domain/src/main/java/com/pocket/domain/usecase/user/LoginUseCase.java b/domain/src/main/java/com/pocket/domain/usecase/user/LoginUseCase.java index f210822..4bac7a8 100644 --- a/domain/src/main/java/com/pocket/domain/usecase/user/LoginUseCase.java +++ b/domain/src/main/java/com/pocket/domain/usecase/user/LoginUseCase.java @@ -1,8 +1,11 @@ package com.pocket.domain.usecase.user; +import com.pocket.domain.dto.user.LoginResponse; import com.pocket.domain.dto.user.UserInfoDTO; public interface LoginUseCase { UserInfoDTO getUserInfo(String name); + + LoginResponse reissueToken(String email); } diff --git a/inbounds/src/main/java/com/pocket/inbounds/user/presentation/UserController.java b/inbounds/src/main/java/com/pocket/inbounds/user/presentation/UserController.java index b8cfccd..0cbc05d 100644 --- a/inbounds/src/main/java/com/pocket/inbounds/user/presentation/UserController.java +++ b/inbounds/src/main/java/com/pocket/inbounds/user/presentation/UserController.java @@ -1,11 +1,12 @@ package com.pocket.inbounds.user.presentation; -import com.pocket.core.aop.annotation.NameAuthenticated; import com.pocket.core.exception.common.ApplicationResponse; +import com.pocket.domain.dto.user.LoginResponse; import com.pocket.domain.dto.user.UserInfoDTO; import com.pocket.domain.usecase.user.LoginUseCase; import com.pocket.inbounds.user.response.UserResponse; import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -27,4 +28,12 @@ public ApplicationResponse getUserInfo( UserResponse response = new UserResponse(userInfoDTO.name(), userInfoDTO.email(), userInfoDTO.image()); return ApplicationResponse.ok(response); } + + @GetMapping(value = "/reissue", produces = MediaType.APPLICATION_JSON_VALUE) + public ApplicationResponse refresh( + @AuthenticationPrincipal UserInfoDTO userInfoDTO + ) { + LoginResponse tokenResponse = loginUseCase.reissueToken(userInfoDTO.email()); + return ApplicationResponse.ok(tokenResponse); + } } diff --git a/inbounds/src/main/java/com/pocket/inbounds/user/presentation/UserControllerDocs.java b/inbounds/src/main/java/com/pocket/inbounds/user/presentation/UserControllerDocs.java index a9a6a58..5c0252b 100644 --- a/inbounds/src/main/java/com/pocket/inbounds/user/presentation/UserControllerDocs.java +++ b/inbounds/src/main/java/com/pocket/inbounds/user/presentation/UserControllerDocs.java @@ -25,4 +25,6 @@ public interface UserControllerDocs { @Operation(summary = "사용자 정보 제공", description = "사용자 정보 전달 API") ApplicationResponse getUserInfo( @AuthenticationPrincipal UserInfoDTO userInfoDTO); + + } diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/user/UserAdapter.java b/outbound/src/main/java/com/pocket/outbound/adapter/user/UserAdapter.java index 52af95c..d09263d 100644 --- a/outbound/src/main/java/com/pocket/outbound/adapter/user/UserAdapter.java +++ b/outbound/src/main/java/com/pocket/outbound/adapter/user/UserAdapter.java @@ -2,10 +2,12 @@ import com.pocket.core.exception.user.UserCustomException; import com.pocket.core.exception.user.UserErrorCode; +import com.pocket.domain.dto.user.LoginResponse; import com.pocket.domain.dto.user.UserInfoDTO; import com.pocket.domain.port.user.LoadUserInfoPort; import com.pocket.outbound.entity.JpaUser; import com.pocket.outbound.repository.UserRepository; +import com.pocket.outbound.util.JwtUtil; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -14,6 +16,7 @@ public class UserAdapter implements LoadUserInfoPort { private final UserRepository userRepository; + private final JwtUtil jwtUtil; public UserInfoDTO loadUserInfo(String name) { JpaUser user = userRepository.findByUserName(name) @@ -22,10 +25,18 @@ public UserInfoDTO loadUserInfo(String name) { return new UserInfoDTO(user.getUser().getName(), user.getUser().getEmail(), user.getUser().getPicture()); } - public UserInfoDTO loadUserInfoByEmail(String email) { - JpaUser user = userRepository.findByUserEmail(email) - .orElseThrow(() -> new UserCustomException(UserErrorCode.NO_USER_INFO)); + public LoginResponse createToken(String email) { - return new UserInfoDTO(user.getUser().getName(), user.getUser().getEmail(), user.getUser().getPicture()); + final JpaUser user = findUser(email); + + String accessToken = jwtUtil.createJwtAccessToken(user.getUser().getEmail(), user.getUser().getSubId()); + String refreshToken = jwtUtil.createJwtRefreshToken(user.getUser().getEmail(), user.getUser().getSubId()); + + return new LoginResponse(accessToken, refreshToken); + } + + private JpaUser findUser(String email) { + return userRepository.findByUserEmail(email) + .orElseThrow(() -> new UserCustomException(UserErrorCode.NO_USER_INFO)); } } diff --git a/outbound/src/main/java/com/pocket/outbound/util/JwtUtil.java b/outbound/src/main/java/com/pocket/outbound/util/JwtUtil.java index e19cd78..eaa5acf 100644 --- a/outbound/src/main/java/com/pocket/outbound/util/JwtUtil.java +++ b/outbound/src/main/java/com/pocket/outbound/util/JwtUtil.java @@ -1,10 +1,13 @@ package com.pocket.outbound.util; import com.pocket.core.exception.jwt.SecurityCustomException; +import com.pocket.core.exception.user.UserCustomException; +import com.pocket.core.exception.user.UserErrorCode; import com.pocket.core.redis.util.RedisUtil; import com.pocket.domain.dto.user.LoginResponse; import com.pocket.domain.dto.user.UserInfoDTO; -import com.pocket.outbound.adapter.user.UserAdapter; +import com.pocket.outbound.entity.JpaUser; +import com.pocket.outbound.repository.UserRepository; import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.SecurityException; @@ -23,7 +26,6 @@ import java.util.Collection; import java.util.Date; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; import java.util.stream.Stream; import static com.pocket.core.exception.jwt.SecurityErrorCode.INVALID_TOKEN; @@ -41,20 +43,20 @@ public class JwtUtil { private final Long accessExpMs; private final Long refreshExpMs; private final RedisUtil redisUtil; - private final UserAdapter userAdapter; + private final UserRepository userRepository; public JwtUtil( @Value("${security.jwt.secret}") String secret, @Value("${security.jwt.token.access-expiration-time}") Long access, @Value("${security.jwt.token.refresh-expiration-time}") Long refresh, RedisUtil redis, - UserAdapter userAdapter) { + UserRepository userRepository) { secretKey = Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)); accessExpMs = access; refreshExpMs = refresh; redisUtil = redis; - this.userAdapter = userAdapter; + this.userRepository = userRepository; } public String createJwtAccessToken(String email, String subId) { @@ -112,10 +114,17 @@ public Authentication resolveToken(String token) { Collection authorities = Stream.of( String.valueOf(claims.get(AUTHORITIES)).split(",")) .map(SimpleGrantedAuthority::new) - .collect(Collectors.toList()); + .toList(); - UserInfoDTO userInfo = userAdapter.loadUserInfoByEmail(getEmail(token)); - return new UsernamePasswordAuthenticationToken(userInfo, token, authorities); + final JpaUser user = findUser(getEmail(token)); + UserInfoDTO infoDTO = new UserInfoDTO(user.getUser().getName(), user.getUser().getEmail(), user.getUser().getPicture()); + + return new UsernamePasswordAuthenticationToken(infoDTO, token, authorities); + } + + private JpaUser findUser(String email) { + return userRepository.findByUserEmail(email) + .orElseThrow(() -> new UserCustomException(UserErrorCode.NO_USER_INFO)); } public String resolveAccessToken(HttpServletRequest request) {