From ea8e4f2af123ba0a513b3f08342df9a9666fd85e Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Thu, 1 Aug 2024 13:47:14 +0900 Subject: [PATCH 01/15] =?UTF-8?q?feat:=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?=EC=84=9C=EB=B2=84=EC=97=90=20=ED=86=A0=ED=81=B0=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserController.java | 21 ++++++++ .../user/dto/request/OauthLoginRequest.java | 4 ++ .../user/dto/request/OauthTokenRequest.java | 10 ++++ .../user/dto/response/OauthTokenResponse.java | 9 ++++ .../bang_ggood/user/service/OauthClient.java | 52 +++++++++++++++++++ .../bang_ggood/user/service/UserService.java | 18 +++++++ 6 files changed, 114 insertions(+) create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/user/controller/UserController.java create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/request/OauthLoginRequest.java create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/request/OauthTokenRequest.java create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthTokenResponse.java create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/user/service/OauthClient.java create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/controller/UserController.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/controller/UserController.java new file mode 100644 index 00000000..3f6075c5 --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/controller/UserController.java @@ -0,0 +1,21 @@ +package com.bang_ggood.user.controller; + +import com.bang_ggood.user.dto.request.OauthLoginRequest; +import com.bang_ggood.user.service.UserService; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; + +@Controller +public class UserController { + + private final UserService userService; + + public UserController(UserService userService) { + this.userService = userService; + } + + @PostMapping("/oauth/login") + public void login(OauthLoginRequest request) { + userService.login(request); + } +} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/request/OauthLoginRequest.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/request/OauthLoginRequest.java new file mode 100644 index 00000000..1ca5d7c1 --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/request/OauthLoginRequest.java @@ -0,0 +1,4 @@ +package com.bang_ggood.user.dto.request; + +public record OauthLoginRequest(String code) { +} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/request/OauthTokenRequest.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/request/OauthTokenRequest.java new file mode 100644 index 00000000..9c9be194 --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/request/OauthTokenRequest.java @@ -0,0 +1,10 @@ +package com.bang_ggood.user.dto.request; + +public record OauthTokenRequest(String grantType, String clientId, + String redirectUri, String code, String clientSecret) { + + public static OauthTokenRequest of(String grantType, String clientId, + String redirectUri, String code, String clientSecret) { + return new OauthTokenRequest(grantType, clientId, redirectUri, code, clientSecret); + } +} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthTokenResponse.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthTokenResponse.java new file mode 100644 index 00000000..01875b77 --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthTokenResponse.java @@ -0,0 +1,9 @@ +package com.bang_ggood.user.dto.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public record OauthTokenResponse(String token_type, String access_token, + String expires_in, String refresh_token, + String refresh_token_expires_in, String scope) { +} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/OauthClient.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/OauthClient.java new file mode 100644 index 00000000..b02fc44d --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/OauthClient.java @@ -0,0 +1,52 @@ +package com.bang_ggood.user.service; + +import com.bang_ggood.user.dto.request.OauthLoginRequest; +import com.bang_ggood.user.dto.response.OauthTokenResponse; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClient; + +@Component +public class OauthClient { + + private final RestClient restClient; + private final String tokenRequestUri; + private final String grantType; + private final String clientId; + private final String clientSecret; + private final String redirectUri; + + public OauthClient( + RestClient restClient, + @Value("${kakao.token_post_uri}") String tokenRequestUri, + @Value("${kakao.grant_type}") String grantType, + @Value("${kakao.client_id}") String clientId, + @Value("${kakao.redirect_uri}")String redirectUrl, + @Value("${kakao.client_secret}")String clientSecret) { + this.restClient = restClient; + this.tokenRequestUri = tokenRequestUri; + this.grantType = grantType; + this.clientId = clientId; + this.redirectUri = redirectUrl; + this.clientSecret = clientSecret; + } + + public OauthTokenResponse requestToken(OauthLoginRequest request) { + MultiValueMap map = new LinkedMultiValueMap<>(); + map.add("grant_type", grantType); + map.add("client_id", clientId); + map.add("redirect_uri", redirectUri); + map.add("code", request.code()); + map.add("client_secret", clientSecret); + + return restClient.post() + .uri(tokenRequestUri) + .contentType(MediaType.APPLICATION_FORM_URLENCODED) + .body(map) + .retrieve() + .body(OauthTokenResponse.class); + } +} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java new file mode 100644 index 00000000..25e2066c --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java @@ -0,0 +1,18 @@ +package com.bang_ggood.user.service; + +import com.bang_ggood.user.dto.request.OauthLoginRequest; +import org.springframework.stereotype.Service; + +@Service +public class UserService { + + private final OauthClient oauthClient; + + public UserService(OauthClient oauthClient) { + this.oauthClient = oauthClient; + } + + public void login(OauthLoginRequest request) { + oauthClient.requestToken(request); + } +} From 4ea13ff4c5bf76fc92bdd43048ca52a7bde4dfb7 Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Thu, 1 Aug 2024 13:48:55 +0900 Subject: [PATCH 02/15] =?UTF-8?q?feat:=20RestClient=20ExceptionHandler=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EB=B0=8F=20RestConfig=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bang_ggood/config/RestClientConfig.java | 17 ++++++++ .../bang_ggood/exception/ExceptionCode.java | 4 +- .../bang_ggood/exception/OauthException.java | 16 +++++++ .../{ => dto}/ExceptionResponse.java | 2 +- .../exception/dto/OauthExceptionResponse.java | 4 ++ .../GlobalExceptionHandler.java | 12 +++++- .../handler/RestClientExceptionHandler.java | 42 +++++++++++++++++++ 7 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/config/RestClientConfig.java create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/exception/OauthException.java rename backend/bang-ggood/src/main/java/com/bang_ggood/exception/{ => dto}/ExceptionResponse.java (69%) create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/exception/dto/OauthExceptionResponse.java rename backend/bang-ggood/src/main/java/com/bang_ggood/{exception => handler}/GlobalExceptionHandler.java (80%) create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/handler/RestClientExceptionHandler.java diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/config/RestClientConfig.java b/backend/bang-ggood/src/main/java/com/bang_ggood/config/RestClientConfig.java new file mode 100644 index 00000000..0006779c --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/config/RestClientConfig.java @@ -0,0 +1,17 @@ +package com.bang_ggood.config; + +import com.bang_ggood.handler.RestClientExceptionHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestClient; + +@Configuration +public class RestClientConfig { + + @Bean + RestClient restClient(RestClientExceptionHandler handler) { + return RestClient.builder() + .defaultStatusHandler(handler) + .build(); + } +} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/exception/ExceptionCode.java b/backend/bang-ggood/src/main/java/com/bang_ggood/exception/ExceptionCode.java index ce8a6dcb..b2798625 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/exception/ExceptionCode.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/exception/ExceptionCode.java @@ -20,7 +20,9 @@ public enum ExceptionCode { // Checklist CHECKLIST_COMPARISON_INVALID_COUNT(HttpStatus.BAD_REQUEST, "비교할 체크리스트 개수가 유효하지 않습니다."), CHECKLIST_NOT_FOUND(HttpStatus.BAD_REQUEST, "체크리스트가 존재하지 않습니다."), - ; + + // Auth + OAUTH_TOKEN_INTERNAL_EXCEPTION(HttpStatus.INTERNAL_SERVER_ERROR, "토큰을 요청하는 과정에서 예상치 못한 예외가 발생했습니다."); private final HttpStatus httpStatus; private final String message; diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/exception/OauthException.java b/backend/bang-ggood/src/main/java/com/bang_ggood/exception/OauthException.java new file mode 100644 index 00000000..bc4d8931 --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/exception/OauthException.java @@ -0,0 +1,16 @@ +package com.bang_ggood.exception; + +import com.bang_ggood.exception.dto.OauthExceptionResponse; + +public class OauthException extends RuntimeException { + + private OauthExceptionResponse response; + + public OauthException(OauthExceptionResponse response) { + this.response = response; + } + + public OauthExceptionResponse getResponse() { + return response; + } +} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/exception/ExceptionResponse.java b/backend/bang-ggood/src/main/java/com/bang_ggood/exception/dto/ExceptionResponse.java similarity index 69% rename from backend/bang-ggood/src/main/java/com/bang_ggood/exception/ExceptionResponse.java rename to backend/bang-ggood/src/main/java/com/bang_ggood/exception/dto/ExceptionResponse.java index bc85a8e7..f23bfa21 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/exception/ExceptionResponse.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/exception/dto/ExceptionResponse.java @@ -1,4 +1,4 @@ -package com.bang_ggood.exception; +package com.bang_ggood.exception.dto; public record ExceptionResponse(String httpMethod, String path, String message) { } diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/exception/dto/OauthExceptionResponse.java b/backend/bang-ggood/src/main/java/com/bang_ggood/exception/dto/OauthExceptionResponse.java new file mode 100644 index 00000000..cdfab8cd --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/exception/dto/OauthExceptionResponse.java @@ -0,0 +1,4 @@ +package com.bang_ggood.exception.dto; + +public record OauthExceptionResponse(String error, String error_description, String error_code) { +} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/exception/GlobalExceptionHandler.java b/backend/bang-ggood/src/main/java/com/bang_ggood/handler/GlobalExceptionHandler.java similarity index 80% rename from backend/bang-ggood/src/main/java/com/bang_ggood/exception/GlobalExceptionHandler.java rename to backend/bang-ggood/src/main/java/com/bang_ggood/handler/GlobalExceptionHandler.java index f7dc1a6c..8c93c140 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/exception/GlobalExceptionHandler.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/handler/GlobalExceptionHandler.java @@ -1,5 +1,9 @@ -package com.bang_ggood.exception; +package com.bang_ggood.handler; +import com.bang_ggood.exception.BangggoodException; +import com.bang_ggood.exception.dto.ExceptionResponse; +import com.bang_ggood.exception.OauthException; +import com.bang_ggood.exception.dto.OauthExceptionResponse; import jakarta.servlet.http.HttpServletRequest; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -44,4 +48,10 @@ public ResponseEntity handleMethodArgumentNotValidException( return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(response); } + + @ExceptionHandler(OauthException.class) + public ResponseEntity handleOauthException(OauthException exception) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(exception.getResponse()); + } } diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/handler/RestClientExceptionHandler.java b/backend/bang-ggood/src/main/java/com/bang_ggood/handler/RestClientExceptionHandler.java new file mode 100644 index 00000000..619cb495 --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/handler/RestClientExceptionHandler.java @@ -0,0 +1,42 @@ +package com.bang_ggood.handler; + +import com.bang_ggood.exception.BangggoodException; +import com.bang_ggood.exception.ExceptionCode; +import com.bang_ggood.exception.OauthException; +import com.bang_ggood.exception.dto.OauthExceptionResponse; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.client.ResponseErrorHandler; +import java.io.IOException; + +@Component +public class RestClientExceptionHandler implements ResponseErrorHandler { + + @Override + public boolean hasError(ClientHttpResponse response) { + try { + return response.getStatusCode().is4xxClientError(); + } catch (IOException exception) { + throw new BangggoodException(ExceptionCode.OAUTH_TOKEN_INTERNAL_EXCEPTION); + } + } + + @Override + public void handleError(ClientHttpResponse response) { + throw new OauthException(getResponseBody(response)); + } + + private OauthExceptionResponse getResponseBody(ClientHttpResponse response) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + + return objectMapper + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .readValue(response.getBody(), OauthExceptionResponse.class); + } catch (IOException exception) { + throw new BangggoodException(ExceptionCode.OAUTH_TOKEN_INTERNAL_EXCEPTION); + } + } +} From 4193e32eacca18e4e727edae17b465fb6e0b8579 Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Thu, 1 Aug 2024 15:59:45 +0900 Subject: [PATCH 03/15] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8A=94=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/dto/response/KakaoAccountResponse.java | 7 +++++++ .../user/dto/response/OauthInfoResponse.java | 7 +++++++ .../user/dto/response/ProfileResponse.java | 7 +++++++ .../com/bang_ggood/user/service/OauthClient.java | 16 +++++++++++++++- .../com/bang_ggood/user/service/UserService.java | 2 +- 5 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/KakaoAccountResponse.java create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthInfoResponse.java create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/ProfileResponse.java diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/KakaoAccountResponse.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/KakaoAccountResponse.java new file mode 100644 index 00000000..7f974b5d --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/KakaoAccountResponse.java @@ -0,0 +1,7 @@ +package com.bang_ggood.user.dto.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public record KakaoAccountResponse(String email, String name, ProfileResponse profile) { +} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthInfoResponse.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthInfoResponse.java new file mode 100644 index 00000000..9491bff3 --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthInfoResponse.java @@ -0,0 +1,7 @@ +package com.bang_ggood.user.dto.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public record OauthInfoResponse(String id, String connected_at, KakaoAccountResponse kakao_account) { +} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/ProfileResponse.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/ProfileResponse.java new file mode 100644 index 00000000..93ef6877 --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/ProfileResponse.java @@ -0,0 +1,7 @@ +package com.bang_ggood.user.dto.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) +public record ProfileResponse(String nickname, String thumbnail_image_url, String profile_image_url) { +} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/OauthClient.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/OauthClient.java index b02fc44d..4a97d848 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/OauthClient.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/OauthClient.java @@ -1,6 +1,7 @@ package com.bang_ggood.user.service; import com.bang_ggood.user.dto.request.OauthLoginRequest; +import com.bang_ggood.user.dto.response.OauthInfoResponse; import com.bang_ggood.user.dto.response.OauthTokenResponse; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; @@ -14,6 +15,7 @@ public class OauthClient { private final RestClient restClient; private final String tokenRequestUri; + private final String userInfoRequestUri; private final String grantType; private final String clientId; private final String clientSecret; @@ -22,19 +24,31 @@ public class OauthClient { public OauthClient( RestClient restClient, @Value("${kakao.token_post_uri}") String tokenRequestUri, + @Value("${kakao.user_get_uri}") String userInfoRequestUri, @Value("${kakao.grant_type}") String grantType, @Value("${kakao.client_id}") String clientId, @Value("${kakao.redirect_uri}")String redirectUrl, @Value("${kakao.client_secret}")String clientSecret) { this.restClient = restClient; this.tokenRequestUri = tokenRequestUri; + this.userInfoRequestUri = userInfoRequestUri; this.grantType = grantType; this.clientId = clientId; this.redirectUri = redirectUrl; this.clientSecret = clientSecret; } - public OauthTokenResponse requestToken(OauthLoginRequest request) { + public OauthInfoResponse requestOauthInfo(OauthLoginRequest request) { + OauthTokenResponse oauthTokenResponse = requestToken(request); + + return restClient.get() + .uri(userInfoRequestUri) + .header("Authorization", "Bearer " + oauthTokenResponse.access_token()) + .retrieve() + .body(OauthInfoResponse.class); + } + + private OauthTokenResponse requestToken(OauthLoginRequest request) { MultiValueMap map = new LinkedMultiValueMap<>(); map.add("grant_type", grantType); map.add("client_id", clientId); diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java index 25e2066c..dc47bfde 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java @@ -13,6 +13,6 @@ public UserService(OauthClient oauthClient) { } public void login(OauthLoginRequest request) { - oauthClient.requestToken(request); + oauthClient.requestOauthInfo(request); } } From 88c50d9f91f32900c649227313a35a430a4ea43d Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Thu, 1 Aug 2024 17:00:34 +0900 Subject: [PATCH 04/15] =?UTF-8?q?fix:=20=EB=A8=B8=EC=A7=80=20=ED=9B=84=20?= =?UTF-8?q?=EC=B6=A9=EB=8F=8C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/bang_ggood/user/controller/UserController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/controller/UserController.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/controller/UserController.java index 3f6075c5..5704e1c5 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/controller/UserController.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/controller/UserController.java @@ -2,10 +2,10 @@ import com.bang_ggood.user.dto.request.OauthLoginRequest; import com.bang_ggood.user.service.UserService; -import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; -@Controller +@RestController public class UserController { private final UserService userService; From 8bbd84bffe51360465ed149ccd08a1ed01a96b5a Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Fri, 2 Aug 2024 15:34:51 +0900 Subject: [PATCH 05/15] =?UTF-8?q?style:=20RestClient=20->=20OauthClient?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{RestClientConfig.java => OauthClientConfig.java} | 6 +++--- ...ceptionHandler.java => OauthClientExceptionHandler.java} | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename backend/bang-ggood/src/main/java/com/bang_ggood/config/{RestClientConfig.java => OauthClientConfig.java} (68%) rename backend/bang-ggood/src/main/java/com/bang_ggood/handler/{RestClientExceptionHandler.java => OauthClientExceptionHandler.java} (95%) diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/config/RestClientConfig.java b/backend/bang-ggood/src/main/java/com/bang_ggood/config/OauthClientConfig.java similarity index 68% rename from backend/bang-ggood/src/main/java/com/bang_ggood/config/RestClientConfig.java rename to backend/bang-ggood/src/main/java/com/bang_ggood/config/OauthClientConfig.java index 0006779c..8bde523e 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/config/RestClientConfig.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/config/OauthClientConfig.java @@ -1,15 +1,15 @@ package com.bang_ggood.config; -import com.bang_ggood.handler.RestClientExceptionHandler; +import com.bang_ggood.handler.OauthClientExceptionHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestClient; @Configuration -public class RestClientConfig { +public class OauthClientConfig { @Bean - RestClient restClient(RestClientExceptionHandler handler) { + RestClient restClient(OauthClientExceptionHandler handler) { return RestClient.builder() .defaultStatusHandler(handler) .build(); diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/handler/RestClientExceptionHandler.java b/backend/bang-ggood/src/main/java/com/bang_ggood/handler/OauthClientExceptionHandler.java similarity index 95% rename from backend/bang-ggood/src/main/java/com/bang_ggood/handler/RestClientExceptionHandler.java rename to backend/bang-ggood/src/main/java/com/bang_ggood/handler/OauthClientExceptionHandler.java index 619cb495..5ff363f2 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/handler/RestClientExceptionHandler.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/handler/OauthClientExceptionHandler.java @@ -12,7 +12,7 @@ import java.io.IOException; @Component -public class RestClientExceptionHandler implements ResponseErrorHandler { +public class OauthClientExceptionHandler implements ResponseErrorHandler { @Override public boolean hasError(ClientHttpResponse response) { From a0f2731c919e96beae15171abf3c2db643961505 Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Fri, 2 Aug 2024 15:36:11 +0900 Subject: [PATCH 06/15] =?UTF-8?q?feat:=20User=20=EC=8A=A4=ED=82=A4?= =?UTF-8?q?=EB=A7=88=EC=97=90=20email=20=ED=95=84=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/bang_ggood/Application.java | 2 ++ .../category/service/CategoryService.java | 2 +- .../controller/ChecklistController.java | 8 ++++---- .../checklist/service/ChecklistService.java | 8 ++++---- .../java/com/bang_ggood/user/domain/User.java | 19 ++++++++++++++++++- .../bang_ggood/user/service/UserService.java | 3 ++- .../bang-ggood/src/main/resources/data.sql | 4 ++-- .../bang-ggood/src/main/resources/schema.sql | 1 + .../checklist/ChecklistFixture.java | 2 +- .../service/ChecklistServiceTest.java | 10 +++++----- .../src/test/resources/data-test.sql | 4 ++-- .../src/test/resources/schema-test.sql | 1 + 12 files changed, 43 insertions(+), 21 deletions(-) diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/Application.java b/backend/bang-ggood/src/main/java/com/bang_ggood/Application.java index c6b0b749..e78a6f00 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/Application.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/Application.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.ConfigurationPropertiesScan; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +@ConfigurationPropertiesScan @EnableJpaAuditing @SpringBootApplication public class Application { diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/category/service/CategoryService.java b/backend/bang-ggood/src/main/java/com/bang_ggood/category/service/CategoryService.java index fbabc62e..e57bbe62 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/category/service/CategoryService.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/category/service/CategoryService.java @@ -30,7 +30,7 @@ public CategoryService(CategoryPriorityRepository categoryPriorityRepository) { @Transactional public void createCategoriesPriority(CategoryPriorityCreateRequest request) { - User user = new User(1L, "방방이"); + User user = new User(1L, "방방이", "bang-ggood@gmail.com"); validate(request); List categoryPriorities = request.categoryIds().stream() .map(id -> new CategoryPriority(id, user)) diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/checklist/controller/ChecklistController.java b/backend/bang-ggood/src/main/java/com/bang_ggood/checklist/controller/ChecklistController.java index 6553639c..b253163f 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/checklist/controller/ChecklistController.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/checklist/controller/ChecklistController.java @@ -9,16 +9,16 @@ import com.bang_ggood.checklist.service.ChecklistService; import com.bang_ggood.user.domain.User; import jakarta.validation.Valid; -import java.net.URI; -import java.util.List; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import java.net.URI; +import java.util.List; @RestController @@ -48,7 +48,7 @@ public ResponseEntity readChecklistById(@PathVariable @GetMapping("/checklists") public ResponseEntity readUserChecklistsPreview() { - User user = new User(1L, "방방이"); + User user = new User(1L, "방방이", "bang-ggood@gmail.com"); return ResponseEntity.ok(checklistService.readUserChecklistsPreview(user)); } diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/checklist/service/ChecklistService.java b/backend/bang-ggood/src/main/java/com/bang_ggood/checklist/service/ChecklistService.java index 606e3130..f002af66 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/checklist/service/ChecklistService.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/checklist/service/ChecklistService.java @@ -72,7 +72,7 @@ public long createChecklist(ChecklistCreateRequest checklistCreateRequest) { Room room = roomRepository.save(checklistCreateRequest.toRoomEntity()); ChecklistInfo checklistInfo = checklistCreateRequest.toChecklistInfo(); - Checklist checklist = new Checklist(new User(1L, "방방이"), room, checklistInfo.deposit(), checklistInfo.rent(), + Checklist checklist = new Checklist(new User(1L, "방방이", "bang-ggood@gmail.com"), room, checklistInfo.deposit(), checklistInfo.rent(), checklistInfo.contractTerm(), checklistInfo.realEstate()); checklistRepository.save(checklist); @@ -126,7 +126,7 @@ private void createChecklistQuestions(ChecklistCreateRequest checklistCreateRequ @Transactional public ChecklistQuestionsResponse readChecklistQuestions() { - User user = new User(1L, "방방이"); + User user = new User(1L, "방방이", "bang-ggood@gmail.com"); List customChecklistQuestions = customChecklistQuestionRepository.findByUser(user); Map> categoryQuestions = customChecklistQuestions.stream() @@ -233,7 +233,7 @@ private List createBadges(List questions) { @Transactional public ChecklistsWithScoreReadResponse readChecklistsComparison(List checklistIds) { - User user = new User(1L, "방끗"); + User user = new User(1L, "방끗", "bang-ggood@gmail.com"); validateChecklistComparison(checklistIds); @@ -290,7 +290,7 @@ public void updateCustomChecklist(CustomChecklistUpdateRequest request) { validateCustomChecklistQuestionsIsNotEmpty(questionIds); validateCustomChecklistQuestionsDuplication(questionIds); - User user = new User(1L, "방방이"); + User user = new User(1L, "방방이", "bang-ggood@gmail.com"); customChecklistQuestionRepository.deleteAllByUser(user); List customChecklistQuestions = questionIds.stream() diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java index 0bb5c85b..01232800 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java @@ -20,9 +20,18 @@ public class User extends BaseEntity { @Column(nullable = false) private String name; - public User(Long id, String name) { + @Column(nullable = false) + private String email; + + public User(String name, String email) { + this.name = name; + this.email = email; + } + + public User(Long id, String name, String email) { // TODO 테스트용 this.id = id; this.name = name; + this.email = email; } public User(String name) { @@ -36,6 +45,14 @@ public Long getId() { return id; } + public String getName() { + return name; + } + + public String getEmail() { + return email; + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java index dc47bfde..392d210d 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java @@ -1,6 +1,7 @@ package com.bang_ggood.user.service; import com.bang_ggood.user.dto.request.OauthLoginRequest; +import com.bang_ggood.user.dto.response.OauthInfoResponse; import org.springframework.stereotype.Service; @Service @@ -13,6 +14,6 @@ public UserService(OauthClient oauthClient) { } public void login(OauthLoginRequest request) { - oauthClient.requestOauthInfo(request); + OauthInfoResponse oauthInfoResponse = oauthClient.requestOauthInfo(request); } } diff --git a/backend/bang-ggood/src/main/resources/data.sql b/backend/bang-ggood/src/main/resources/data.sql index c0813148..80cd9ff3 100644 --- a/backend/bang-ggood/src/main/resources/data.sql +++ b/backend/bang-ggood/src/main/resources/data.sql @@ -1,5 +1,5 @@ -INSERT INTO users(id, name, created_at, modified_at, deleted) -VALUES (1, '방방이', '2024-07-22 07:56:42', '2024-07-22 07:56:42', false); +INSERT INTO users(id, name, email, created_at, modified_at, deleted) +VALUES (1, '방방이', 'bang-ggood@gmail.com', '2024-07-22 07:56:42', '2024-07-22 07:56:42', false); INSERT INTO custom_checklist_question(user_id, question, created_at, modified_at, deleted) VALUES (1, 'CLEAN_1', '2024-07-22 07:56:42', '2024-07-22 07:56:42', false), diff --git a/backend/bang-ggood/src/main/resources/schema.sql b/backend/bang-ggood/src/main/resources/schema.sql index f7456c8d..17fcfeac 100644 --- a/backend/bang-ggood/src/main/resources/schema.sql +++ b/backend/bang-ggood/src/main/resources/schema.sql @@ -29,6 +29,7 @@ CREATE TABLE users ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL, created_at TIMESTAMP(6), modified_at TIMESTAMP(6), deleted BOOLEAN diff --git a/backend/bang-ggood/src/test/java/com/bang_ggood/checklist/ChecklistFixture.java b/backend/bang-ggood/src/test/java/com/bang_ggood/checklist/ChecklistFixture.java index dca95ec5..33f83da4 100644 --- a/backend/bang-ggood/src/test/java/com/bang_ggood/checklist/ChecklistFixture.java +++ b/backend/bang-ggood/src/test/java/com/bang_ggood/checklist/ChecklistFixture.java @@ -11,7 +11,7 @@ public class ChecklistFixture { public static final Checklist checklist = new Checklist( - new User(1L, "방방이"), + new User(1L, "방방이", "bang-ggood@gmail.com"), RoomFixture.ROOM_1, 1000, 50, 12, "방끗공인중개사" ); diff --git a/backend/bang-ggood/src/test/java/com/bang_ggood/checklist/service/ChecklistServiceTest.java b/backend/bang-ggood/src/test/java/com/bang_ggood/checklist/service/ChecklistServiceTest.java index 46aae9ed..3e7ec4df 100644 --- a/backend/bang-ggood/src/test/java/com/bang_ggood/checklist/service/ChecklistServiceTest.java +++ b/backend/bang-ggood/src/test/java/com/bang_ggood/checklist/service/ChecklistServiceTest.java @@ -4,8 +4,8 @@ import com.bang_ggood.category.domain.Category; import com.bang_ggood.checklist.ChecklistFixture; import com.bang_ggood.checklist.domain.Checklist; -import com.bang_ggood.checklist.dto.request.CustomChecklistUpdateRequest; import com.bang_ggood.checklist.dto.request.ChecklistCreateRequest; +import com.bang_ggood.checklist.dto.request.CustomChecklistUpdateRequest; import com.bang_ggood.checklist.dto.response.ChecklistQuestionsResponse; import com.bang_ggood.checklist.dto.response.ChecklistsWithScoreReadResponse; import com.bang_ggood.checklist.dto.response.SelectedChecklistResponse; @@ -18,10 +18,10 @@ import com.bang_ggood.room.domain.Room; import com.bang_ggood.room.repository.RoomRepository; import com.bang_ggood.user.domain.User; -import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import java.util.List; import static com.bang_ggood.checklist.CustomChecklistFixture.CUSTOM_CHECKLIST_UPDATE_REQUEST; import static com.bang_ggood.checklist.CustomChecklistFixture.CUSTOM_CHECKLIST_UPDATE_REQUEST_DUPLICATED; @@ -206,7 +206,7 @@ void readChecklistById_invalidChecklistId_exception() { @Test void readChecklistsComparison() { // given - User user = new User(1L, "방방이"); + User user = new User(1L, "방방이", "bang-ggood@gmail.com"); Room room1 = RoomFixture.ROOM_1; Room room2 = RoomFixture.ROOM_2; Room room3 = RoomFixture.ROOM_3; @@ -242,7 +242,7 @@ void readChecklistsComparison_invalidIdCount() { @Test void readChecklistsComparison_invalidId() { // given - User user = new User(1L, "방방이"); + User user = new User(1L, "방방이", "bang-ggood@gmail.com"); Room room1 = RoomFixture.ROOM_1; Room room2 = RoomFixture.ROOM_2; Room room3 = RoomFixture.ROOM_3; @@ -271,7 +271,7 @@ void updateCustomChecklist() { checklistService.updateCustomChecklist(request); // then - assertThat(customChecklistQuestionRepository.findByUser(new User(1L, "방방이"))) + assertThat(customChecklistQuestionRepository.findByUser(new User(1L, "방방이", "bang-ggood@gmail.com"))) .hasSize(request.questionIds().size()); } diff --git a/backend/bang-ggood/src/test/resources/data-test.sql b/backend/bang-ggood/src/test/resources/data-test.sql index c0813148..80cd9ff3 100644 --- a/backend/bang-ggood/src/test/resources/data-test.sql +++ b/backend/bang-ggood/src/test/resources/data-test.sql @@ -1,5 +1,5 @@ -INSERT INTO users(id, name, created_at, modified_at, deleted) -VALUES (1, '방방이', '2024-07-22 07:56:42', '2024-07-22 07:56:42', false); +INSERT INTO users(id, name, email, created_at, modified_at, deleted) +VALUES (1, '방방이', 'bang-ggood@gmail.com', '2024-07-22 07:56:42', '2024-07-22 07:56:42', false); INSERT INTO custom_checklist_question(user_id, question, created_at, modified_at, deleted) VALUES (1, 'CLEAN_1', '2024-07-22 07:56:42', '2024-07-22 07:56:42', false), diff --git a/backend/bang-ggood/src/test/resources/schema-test.sql b/backend/bang-ggood/src/test/resources/schema-test.sql index ee70c75d..4348b96c 100644 --- a/backend/bang-ggood/src/test/resources/schema-test.sql +++ b/backend/bang-ggood/src/test/resources/schema-test.sql @@ -30,6 +30,7 @@ CREATE TABLE users ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL, created_at TIMESTAMP(6), modified_at TIMESTAMP(6), deleted BOOLEAN From 4dad808af1055fdc7f6f60232fa0c4775a8f48f6 Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Fri, 2 Aug 2024 15:36:35 +0900 Subject: [PATCH 07/15] =?UTF-8?q?chore:=20jwt=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/bang-ggood/build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/bang-ggood/build.gradle b/backend/bang-ggood/build.gradle index 543fff75..d6339581 100644 --- a/backend/bang-ggood/build.gradle +++ b/backend/bang-ggood/build.gradle @@ -22,7 +22,10 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0' implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'io.jsonwebtoken:jjwt-api:0.11.2' + implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' runtimeOnly 'com.h2database:h2' + testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.rest-assured:rest-assured:5.3.1' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' From 5d97202f73e3adb86f0d2fcc9180cea1b560c407 Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Sat, 3 Aug 2024 12:56:53 +0900 Subject: [PATCH 08/15] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/bang-ggood/build.gradle | 1 + .../java/com/bang_ggood/user/domain/User.java | 6 ++ .../user/dto/response/OauthInfoResponse.java | 5 ++ .../user/repository/UserRepository.java | 3 + .../user/service/JwtTokenProvider.java | 36 +++++++++++ .../bang_ggood/user/service/UserService.java | 15 ++++- .../java/com/bang_ggood/user/UserFixture.java | 20 +++++++ .../user/service/JwtTokenProviderTest.java | 27 +++++++++ .../user/service/UserServiceTest.java | 59 +++++++++++++++++++ .../src/test/resources/data-test.sql | 4 +- 10 files changed, 172 insertions(+), 4 deletions(-) create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/user/service/JwtTokenProvider.java create mode 100644 backend/bang-ggood/src/test/java/com/bang_ggood/user/UserFixture.java create mode 100644 backend/bang-ggood/src/test/java/com/bang_ggood/user/service/JwtTokenProviderTest.java create mode 100644 backend/bang-ggood/src/test/java/com/bang_ggood/user/service/UserServiceTest.java diff --git a/backend/bang-ggood/build.gradle b/backend/bang-ggood/build.gradle index d6339581..bce95f21 100644 --- a/backend/bang-ggood/build.gradle +++ b/backend/bang-ggood/build.gradle @@ -24,6 +24,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'io.jsonwebtoken:jjwt-api:0.11.2' implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' + implementation 'io.jsonwebtoken:jjwt-gson:0.11.2' runtimeOnly 'com.h2database:h2' testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java index 01232800..12619c33 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java @@ -23,9 +23,12 @@ public class User extends BaseEntity { @Column(nullable = false) private String email; + private Boolean deleted; + public User(String name, String email) { this.name = name; this.email = email; + this.deleted = Boolean.FALSE; } public User(Long id, String name, String email) { // TODO 테스트용 @@ -36,6 +39,7 @@ public User(Long id, String name, String email) { // TODO 테스트용 public User(String name) { this.name = name; + this.deleted = Boolean.FALSE; } protected User() { @@ -75,6 +79,8 @@ public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + + ", email='" + email + '\'' + + ", deleted=" + deleted + '}'; } } diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthInfoResponse.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthInfoResponse.java index 9491bff3..5f942b96 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthInfoResponse.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthInfoResponse.java @@ -1,7 +1,12 @@ package com.bang_ggood.user.dto.response; +import com.bang_ggood.user.domain.User; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public record OauthInfoResponse(String id, String connected_at, KakaoAccountResponse kakao_account) { + + public User toUserEntity() { + return new User(kakao_account().name(), kakao_account.email()); + } } diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/repository/UserRepository.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/repository/UserRepository.java index 65cadda4..596d6674 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/repository/UserRepository.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/repository/UserRepository.java @@ -4,10 +4,13 @@ import com.bang_ggood.exception.ExceptionCode; import com.bang_ggood.user.domain.User; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; public interface UserRepository extends JpaRepository { default User getUserById(Long id) { return findById(id).orElseThrow(() -> new BangggoodException(ExceptionCode.USER_NOT_FOUND)); } + + Optional findByEmail(String email); } diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/JwtTokenProvider.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/JwtTokenProvider.java new file mode 100644 index 00000000..4d916ad0 --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/JwtTokenProvider.java @@ -0,0 +1,36 @@ +package com.bang_ggood.user.service; + +import com.bang_ggood.user.domain.User; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import java.util.Date; + +@Component +public class JwtTokenProvider { + + private final String secretKey; + private final int tokenExpirationMills; + + public JwtTokenProvider( + @Value("${jwt.secret-key}") String secretKey, + @Value("${jwt.expiration-millis}") int tokenExpirationMills) { + this.secretKey = secretKey; + this.tokenExpirationMills = tokenExpirationMills; + } + + public String createToken(User user) { + Date now = new Date(); + Date expiredDate = new Date(now.getTime() + tokenExpirationMills); + + return Jwts.builder() + .claim("id", user.getId()) + .claim("name", user.getName()) + .claim("email", user.getEmail()) + .setIssuedAt(now) + .setExpiration(expiredDate) + .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) + .compact(); + } +} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java index 392d210d..903b1dc7 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/UserService.java @@ -1,19 +1,30 @@ package com.bang_ggood.user.service; +import com.bang_ggood.user.domain.User; import com.bang_ggood.user.dto.request.OauthLoginRequest; import com.bang_ggood.user.dto.response.OauthInfoResponse; +import com.bang_ggood.user.repository.UserRepository; import org.springframework.stereotype.Service; @Service public class UserService { private final OauthClient oauthClient; + private final JwtTokenProvider jwtTokenProvider; + private final UserRepository userRepository; - public UserService(OauthClient oauthClient) { + public UserService(OauthClient oauthClient, JwtTokenProvider jwtTokenProvider, UserRepository userRepository) { this.oauthClient = oauthClient; + this.jwtTokenProvider = jwtTokenProvider; + this.userRepository = userRepository; } - public void login(OauthLoginRequest request) { + public String login(OauthLoginRequest request) { OauthInfoResponse oauthInfoResponse = oauthClient.requestOauthInfo(request); + + User user = userRepository.findByEmail(oauthInfoResponse.kakao_account().email()) + .orElseGet(() -> userRepository.save(oauthInfoResponse.toUserEntity())); + + return jwtTokenProvider.createToken(user); } } diff --git a/backend/bang-ggood/src/test/java/com/bang_ggood/user/UserFixture.java b/backend/bang-ggood/src/test/java/com/bang_ggood/user/UserFixture.java new file mode 100644 index 00000000..65a5c11c --- /dev/null +++ b/backend/bang-ggood/src/test/java/com/bang_ggood/user/UserFixture.java @@ -0,0 +1,20 @@ +package com.bang_ggood.user; + +import com.bang_ggood.user.domain.User; +import com.bang_ggood.user.dto.response.KakaoAccountResponse; +import com.bang_ggood.user.dto.response.OauthInfoResponse; +import com.bang_ggood.user.dto.response.ProfileResponse; + +public class UserFixture { + + public static final User USER1 = new User("방방이", "bang-bang@gmail.com"); + public static final User USER2 = new User("빵빵이", "bbang-bbang@gmail.com"); + public static final OauthInfoResponse oauthInfoResponseUSER1 = new OauthInfoResponse("", "", + new KakaoAccountResponse(USER1.getEmail(), USER1.getName(), + new ProfileResponse("", "",""))); + + public static final OauthInfoResponse oauthInfoResponseUSER2 = new OauthInfoResponse("", "", + new KakaoAccountResponse(USER2.getEmail(), USER2.getName(), + new ProfileResponse("", "",""))); + +} diff --git a/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/JwtTokenProviderTest.java b/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/JwtTokenProviderTest.java new file mode 100644 index 00000000..d9c447ef --- /dev/null +++ b/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/JwtTokenProviderTest.java @@ -0,0 +1,27 @@ +package com.bang_ggood.user.service; + +import com.bang_ggood.IntegrationTestSupport; +import com.bang_ggood.user.domain.User; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class JwtTokenProviderTest extends IntegrationTestSupport { + + @Autowired + private JwtTokenProvider jwtTokenProvider; + + @DisplayName("토큰 생성 성공") + @Test + void createToken() { + // given + User user = new User("방끗", "bang-ggood@gmail.com"); + + // when + String token = jwtTokenProvider.createToken(user); + + // then + Assertions.assertThat(token).isNotEmpty(); + } +} diff --git a/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/UserServiceTest.java b/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/UserServiceTest.java new file mode 100644 index 00000000..77c5de9a --- /dev/null +++ b/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/UserServiceTest.java @@ -0,0 +1,59 @@ +package com.bang_ggood.user.service; + +import com.bang_ggood.IntegrationTestSupport; +import com.bang_ggood.user.UserFixture; +import com.bang_ggood.user.dto.request.OauthLoginRequest; +import com.bang_ggood.user.repository.UserRepository; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; + +import static com.bang_ggood.user.UserFixture.USER1; +import static com.bang_ggood.user.UserFixture.oauthInfoResponseUSER1; + +@ExtendWith(MockitoExtension.class) +class UserServiceTest extends IntegrationTestSupport { + + @MockBean + private OauthClient oauthClient; + + @Autowired + private UserService userService; + + @Autowired + private UserRepository userRepository; + + @DisplayName("로그인 성공 : 존재하지 않는 회원이면 데이터베이스에 새로운 유저를 추가하고 토큰을 반환한다.") + @Test + void login_signup() { + // given + Mockito.when(oauthClient.requestOauthInfo(new OauthLoginRequest(""))) + .thenReturn(UserFixture.oauthInfoResponseUSER2); + + // when + String token = userService.login(new OauthLoginRequest("")); + + // then + Assertions.assertThat(token).isNotBlank(); + } + + @DisplayName("로그인 성공 : 존재하는 회원이면 데이터베이스에 새로운 유저를 추가하지않고 토큰을 바로 반환한다.") + @Test + void login() { + // given + userRepository.save(USER1); + Mockito.when(oauthClient.requestOauthInfo(new OauthLoginRequest(""))) + .thenReturn(oauthInfoResponseUSER1); + + // when + String token = userService.login(new OauthLoginRequest("")); + + // then + Assertions.assertThat(token).isNotBlank(); + } +} diff --git a/backend/bang-ggood/src/test/resources/data-test.sql b/backend/bang-ggood/src/test/resources/data-test.sql index 80cd9ff3..44bcaa2f 100644 --- a/backend/bang-ggood/src/test/resources/data-test.sql +++ b/backend/bang-ggood/src/test/resources/data-test.sql @@ -1,5 +1,5 @@ -INSERT INTO users(id, name, email, created_at, modified_at, deleted) -VALUES (1, '방방이', 'bang-ggood@gmail.com', '2024-07-22 07:56:42', '2024-07-22 07:56:42', false); +INSERT INTO users(name, email, created_at, modified_at, deleted) +VALUES ('방방이', 'bang-ggood@gmail.com', '2024-07-22 07:56:42', '2024-07-22 07:56:42', false); INSERT INTO custom_checklist_question(user_id, question, created_at, modified_at, deleted) VALUES (1, 'CLEAN_1', '2024-07-22 07:56:42', '2024-07-22 07:56:42', false), From c1120da01058d65f495e05ec246728ed8c8a5452 Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Sat, 3 Aug 2024 14:05:09 +0900 Subject: [PATCH 09/15] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=ED=95=98=EB=8A=94=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84=20=EB=B0=8F?= =?UTF-8?q?=20Mock=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserController.java | 18 ++++++++-- .../java/com/bang_ggood/user/domain/User.java | 5 --- .../user/dto/request/OauthLoginRequest.java | 4 ++- .../bang_ggood/AcceptanceMockTestSupport.java | 30 ++++++++++++++++ .../user/controller/LoginMockE2ETest.java | 35 +++++++++++++++++++ .../user/controller/UserE2ETest.java | 22 ++++++++++++ 6 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 backend/bang-ggood/src/test/java/com/bang_ggood/AcceptanceMockTestSupport.java create mode 100644 backend/bang-ggood/src/test/java/com/bang_ggood/user/controller/LoginMockE2ETest.java create mode 100644 backend/bang-ggood/src/test/java/com/bang_ggood/user/controller/UserE2ETest.java diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/controller/UserController.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/controller/UserController.java index 5704e1c5..bf94bbc9 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/controller/UserController.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/controller/UserController.java @@ -2,12 +2,18 @@ import com.bang_ggood.user.dto.request.OauthLoginRequest; import com.bang_ggood.user.service.UserService; +import jakarta.validation.Valid; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseCookie; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { + public static final String TOKEN_COOKIE_NAME = "token"; private final UserService userService; public UserController(UserService userService) { @@ -15,7 +21,15 @@ public UserController(UserService userService) { } @PostMapping("/oauth/login") - public void login(OauthLoginRequest request) { - userService.login(request); + public ResponseEntity login(@Valid @RequestBody OauthLoginRequest request) { + String token = userService.login(request); + + ResponseCookie cookie = ResponseCookie + .from(TOKEN_COOKIE_NAME, token) + .httpOnly(true) + .path("/") + .build(); + + return ResponseEntity.ok().header(HttpHeaders.SET_COOKIE, cookie.toString()).build(); } } diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java index 12619c33..f9ef142b 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java @@ -37,11 +37,6 @@ public User(Long id, String name, String email) { // TODO 테스트용 this.email = email; } - public User(String name) { - this.name = name; - this.deleted = Boolean.FALSE; - } - protected User() { } diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/request/OauthLoginRequest.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/request/OauthLoginRequest.java index 1ca5d7c1..c108d4d0 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/request/OauthLoginRequest.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/request/OauthLoginRequest.java @@ -1,4 +1,6 @@ package com.bang_ggood.user.dto.request; -public record OauthLoginRequest(String code) { +import jakarta.validation.constraints.NotBlank; + +public record OauthLoginRequest(@NotBlank(message = "인가 코드가 존재하지 않습니다.") String code) { } diff --git a/backend/bang-ggood/src/test/java/com/bang_ggood/AcceptanceMockTestSupport.java b/backend/bang-ggood/src/test/java/com/bang_ggood/AcceptanceMockTestSupport.java new file mode 100644 index 00000000..0176e873 --- /dev/null +++ b/backend/bang-ggood/src/test/java/com/bang_ggood/AcceptanceMockTestSupport.java @@ -0,0 +1,30 @@ +package com.bang_ggood; + +import io.restassured.RestAssured; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.web.servlet.MockMvc; + +@ActiveProfiles("test") +@Sql(scripts = {"/schema-test.sql", "/data-test.sql"}) +@AutoConfigureMockMvc +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +public abstract class AcceptanceMockTestSupport { + + @Autowired + protected MockMvc mockMvc; + + @LocalServerPort + protected int port; + + @BeforeEach + void setPort() { + RestAssured.port = port; + } +} diff --git a/backend/bang-ggood/src/test/java/com/bang_ggood/user/controller/LoginMockE2ETest.java b/backend/bang-ggood/src/test/java/com/bang_ggood/user/controller/LoginMockE2ETest.java new file mode 100644 index 00000000..61a70ac0 --- /dev/null +++ b/backend/bang-ggood/src/test/java/com/bang_ggood/user/controller/LoginMockE2ETest.java @@ -0,0 +1,35 @@ +package com.bang_ggood.user.controller; + +import com.bang_ggood.AcceptanceMockTestSupport; +import com.bang_ggood.user.dto.request.OauthLoginRequest; +import com.bang_ggood.user.service.UserService; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + +import static org.mockito.ArgumentMatchers.any; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class LoginMockE2ETest extends AcceptanceMockTestSupport { + + @MockBean + UserService userService; + + @DisplayName("로그인 성공") + @Test + void login() throws Exception { + String testToken = "testToken"; + Mockito.when(userService.login(any(OauthLoginRequest.class))).thenReturn(testToken); + + mockMvc.perform(post("/oauth/login") + .contentType(MediaType.APPLICATION_JSON) + .content("{\"code\":\"code\"}")) + .andExpect(status().isOk()) + .andExpect(header().string(HttpHeaders.SET_COOKIE, "token=" + testToken + "; Path=/; HttpOnly")); + } +} diff --git a/backend/bang-ggood/src/test/java/com/bang_ggood/user/controller/UserE2ETest.java b/backend/bang-ggood/src/test/java/com/bang_ggood/user/controller/UserE2ETest.java new file mode 100644 index 00000000..929cb302 --- /dev/null +++ b/backend/bang-ggood/src/test/java/com/bang_ggood/user/controller/UserE2ETest.java @@ -0,0 +1,22 @@ +package com.bang_ggood.user.controller; + +import com.bang_ggood.AcceptanceTest; +import com.bang_ggood.user.dto.request.OauthLoginRequest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class UserE2ETest extends AcceptanceTest { + + @DisplayName("로그인 실패 : 인가코드가 없는 경우") + @Test + void login_code_notBlank_exception() { + RestAssured.given().log().all() + .contentType(ContentType.JSON) + .body(new OauthLoginRequest("")) + .when().post("/oauth/login") + .then().log().all() + .statusCode(400); + } +} From 76426ba946778809fed8690d0711b3199dbf4036 Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Sat, 3 Aug 2024 16:10:25 +0900 Subject: [PATCH 10/15] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=94=BD=EC=8A=A4=EC=B2=98=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/bang_ggood/Application.java | 2 -- .../com/bang_ggood/user/service/UserServiceTest.java | 11 +++++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/Application.java b/backend/bang-ggood/src/main/java/com/bang_ggood/Application.java index e78a6f00..c6b0b749 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/Application.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/Application.java @@ -2,10 +2,8 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.ConfigurationPropertiesScan; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; -@ConfigurationPropertiesScan @EnableJpaAuditing @SpringBootApplication public class Application { diff --git a/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/UserServiceTest.java b/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/UserServiceTest.java index 77c5de9a..e6889a12 100644 --- a/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/UserServiceTest.java +++ b/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/UserServiceTest.java @@ -15,6 +15,7 @@ import static com.bang_ggood.user.UserFixture.USER1; import static com.bang_ggood.user.UserFixture.oauthInfoResponseUSER1; +import static org.mockito.ArgumentMatchers.any; @ExtendWith(MockitoExtension.class) class UserServiceTest extends IntegrationTestSupport { @@ -28,15 +29,17 @@ class UserServiceTest extends IntegrationTestSupport { @Autowired private UserRepository userRepository; + private static final OauthLoginRequest oauthLoginRequest = new OauthLoginRequest("testCode"); + @DisplayName("로그인 성공 : 존재하지 않는 회원이면 데이터베이스에 새로운 유저를 추가하고 토큰을 반환한다.") @Test void login_signup() { // given - Mockito.when(oauthClient.requestOauthInfo(new OauthLoginRequest(""))) + Mockito.when(oauthClient.requestOauthInfo(any(OauthLoginRequest.class))) .thenReturn(UserFixture.oauthInfoResponseUSER2); // when - String token = userService.login(new OauthLoginRequest("")); + String token = userService.login(oauthLoginRequest); // then Assertions.assertThat(token).isNotBlank(); @@ -47,11 +50,11 @@ void login_signup() { void login() { // given userRepository.save(USER1); - Mockito.when(oauthClient.requestOauthInfo(new OauthLoginRequest(""))) + Mockito.when(oauthClient.requestOauthInfo(any(OauthLoginRequest.class))) .thenReturn(oauthInfoResponseUSER1); // when - String token = userService.login(new OauthLoginRequest("")); + String token = userService.login(oauthLoginRequest); // then Assertions.assertThat(token).isNotBlank(); From 32c5fdfe866306f50fb5eeee84afbdc7b747b786 Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Sat, 3 Aug 2024 23:15:52 +0900 Subject: [PATCH 11/15] =?UTF-8?q?refactor:=20User=20deleted=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/bang_ggood/user/domain/User.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java index f9ef142b..56c355ed 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/domain/User.java @@ -23,12 +23,9 @@ public class User extends BaseEntity { @Column(nullable = false) private String email; - private Boolean deleted; - public User(String name, String email) { this.name = name; this.email = email; - this.deleted = Boolean.FALSE; } public User(Long id, String name, String email) { // TODO 테스트용 @@ -75,7 +72,6 @@ public String toString() { "id=" + id + ", name='" + name + '\'' + ", email='" + email + '\'' + - ", deleted=" + deleted + '}'; } } From ebec67c3e8e38c0750c40276cd642304434a48f1 Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Sat, 3 Aug 2024 23:49:09 +0900 Subject: [PATCH 12/15] =?UTF-8?q?feat:=20=ED=86=A0=ED=81=B0=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=ED=95=98=EB=8A=94=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/dto/response/OauthInfoResponse.java | 2 +- .../com/bang_ggood/user/service/AuthInfo.java | 11 ++++++++ .../user/service/JwtTokenProvider.java | 25 ++++++++++++++++--- .../user/service/JwtTokenProviderTest.java | 18 ++++++++++--- 4 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/user/service/AuthInfo.java diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthInfoResponse.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthInfoResponse.java index 5f942b96..fd88506e 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthInfoResponse.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/dto/response/OauthInfoResponse.java @@ -7,6 +7,6 @@ public record OauthInfoResponse(String id, String connected_at, KakaoAccountResponse kakao_account) { public User toUserEntity() { - return new User(kakao_account().name(), kakao_account.email()); + return new User(kakao_account.name(), kakao_account.email()); } } diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/AuthInfo.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/AuthInfo.java new file mode 100644 index 00000000..7c9b2894 --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/AuthInfo.java @@ -0,0 +1,11 @@ +package com.bang_ggood.user.service; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +public record AuthInfo(@NotNull Long id, @NotBlank String name, @NotBlank String email) { + + public static AuthInfo of(Long id, String name, String email) { + return new AuthInfo(id, name, email); + } +} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/JwtTokenProvider.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/JwtTokenProvider.java index 4d916ad0..e1753505 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/JwtTokenProvider.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/JwtTokenProvider.java @@ -1,6 +1,7 @@ package com.bang_ggood.user.service; import com.bang_ggood.user.domain.User; +import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import org.springframework.beans.factory.annotation.Value; @@ -10,6 +11,10 @@ @Component public class JwtTokenProvider { + private static final String ID_KEY = "id"; + private static final String NAME_KEY = "name"; + private static final String EMAIL_KEY = "email"; + private final String secretKey; private final int tokenExpirationMills; @@ -25,12 +30,26 @@ public String createToken(User user) { Date expiredDate = new Date(now.getTime() + tokenExpirationMills); return Jwts.builder() - .claim("id", user.getId()) - .claim("name", user.getName()) - .claim("email", user.getEmail()) + .claim(ID_KEY, user.getId().toString()) + .claim(NAME_KEY, user.getName()) + .claim(EMAIL_KEY, user.getEmail()) .setIssuedAt(now) .setExpiration(expiredDate) .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) .compact(); } + + public AuthInfo resolveToken(String token) { + Claims claims = Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) + .build() + .parseClaimsJws(token) + .getBody(); + + Long id = Long.valueOf(claims.get(ID_KEY).toString()); + String name = claims.get(NAME_KEY).toString(); + String email = claims.get(EMAIL_KEY).toString(); + + return AuthInfo.of(id, name, email); + } } diff --git a/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/JwtTokenProviderTest.java b/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/JwtTokenProviderTest.java index d9c447ef..5cb4092b 100644 --- a/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/JwtTokenProviderTest.java +++ b/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/JwtTokenProviderTest.java @@ -2,26 +2,36 @@ import com.bang_ggood.IntegrationTestSupport; import com.bang_ggood.user.domain.User; -import org.assertj.core.api.Assertions; +import com.bang_ggood.user.repository.UserRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import static com.bang_ggood.user.UserFixture.USER1; +import static org.junit.jupiter.api.Assertions.assertAll; + class JwtTokenProviderTest extends IntegrationTestSupport { @Autowired private JwtTokenProvider jwtTokenProvider; + @Autowired + private UserRepository userRepository; @DisplayName("토큰 생성 성공") @Test void createToken() { // given - User user = new User("방끗", "bang-ggood@gmail.com"); + User user = userRepository.save(USER1); + String token = jwtTokenProvider.createToken(user); // when - String token = jwtTokenProvider.createToken(user); + AuthInfo authInfo = jwtTokenProvider.resolveToken(token); // then - Assertions.assertThat(token).isNotEmpty(); + assertAll( + () -> authInfo.id().equals(user.getId()), + () -> authInfo.name().equals(user.getName()), + () -> authInfo.email().equals(user.getEmail()) + ); } } From 0392c9e011dedfc22524143317bbaeb4b9b92c5c Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Sun, 4 Aug 2024 14:07:03 +0900 Subject: [PATCH 13/15] =?UTF-8?q?refactor:=20jwt=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=EC=97=90=20id=EA=B0=92=EB=A7=8C=20=EB=8B=B4?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/bang_ggood/user/service/AuthInfo.java | 11 ----------- .../com/bang_ggood/user/service/AuthUser.java | 10 ++++++++++ .../user/service/JwtTokenProvider.java | 17 ++++------------- .../user/service/JwtTokenProviderTest.java | 10 +++------- 4 files changed, 17 insertions(+), 31 deletions(-) delete mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/user/service/AuthInfo.java create mode 100644 backend/bang-ggood/src/main/java/com/bang_ggood/user/service/AuthUser.java diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/AuthInfo.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/AuthInfo.java deleted file mode 100644 index 7c9b2894..00000000 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/AuthInfo.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.bang_ggood.user.service; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; - -public record AuthInfo(@NotNull Long id, @NotBlank String name, @NotBlank String email) { - - public static AuthInfo of(Long id, String name, String email) { - return new AuthInfo(id, name, email); - } -} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/AuthUser.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/AuthUser.java new file mode 100644 index 00000000..caa622bf --- /dev/null +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/AuthUser.java @@ -0,0 +1,10 @@ +package com.bang_ggood.user.service; + +import jakarta.validation.constraints.NotNull; + +public record AuthUser(@NotNull Long id) { + + public static AuthUser from(Long id) { + return new AuthUser(id); + } +} diff --git a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/JwtTokenProvider.java b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/JwtTokenProvider.java index e1753505..f94f450a 100644 --- a/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/JwtTokenProvider.java +++ b/backend/bang-ggood/src/main/java/com/bang_ggood/user/service/JwtTokenProvider.java @@ -11,10 +11,6 @@ @Component public class JwtTokenProvider { - private static final String ID_KEY = "id"; - private static final String NAME_KEY = "name"; - private static final String EMAIL_KEY = "email"; - private final String secretKey; private final int tokenExpirationMills; @@ -30,26 +26,21 @@ public String createToken(User user) { Date expiredDate = new Date(now.getTime() + tokenExpirationMills); return Jwts.builder() - .claim(ID_KEY, user.getId().toString()) - .claim(NAME_KEY, user.getName()) - .claim(EMAIL_KEY, user.getEmail()) + .setSubject(user.getId().toString()) .setIssuedAt(now) .setExpiration(expiredDate) .signWith(Keys.hmacShaKeyFor(secretKey.getBytes())) .compact(); } - public AuthInfo resolveToken(String token) { + public AuthUser resolveToken(String token) { Claims claims = Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())) .build() .parseClaimsJws(token) .getBody(); - Long id = Long.valueOf(claims.get(ID_KEY).toString()); - String name = claims.get(NAME_KEY).toString(); - String email = claims.get(EMAIL_KEY).toString(); - - return AuthInfo.of(id, name, email); + Long id = Long.valueOf(claims.getSubject()); + return AuthUser.from(id); } } diff --git a/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/JwtTokenProviderTest.java b/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/JwtTokenProviderTest.java index 5cb4092b..ed28534c 100644 --- a/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/JwtTokenProviderTest.java +++ b/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/JwtTokenProviderTest.java @@ -3,12 +3,12 @@ import com.bang_ggood.IntegrationTestSupport; import com.bang_ggood.user.domain.User; import com.bang_ggood.user.repository.UserRepository; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import static com.bang_ggood.user.UserFixture.USER1; -import static org.junit.jupiter.api.Assertions.assertAll; class JwtTokenProviderTest extends IntegrationTestSupport { @@ -25,13 +25,9 @@ void createToken() { String token = jwtTokenProvider.createToken(user); // when - AuthInfo authInfo = jwtTokenProvider.resolveToken(token); + AuthUser authUser = jwtTokenProvider.resolveToken(token); // then - assertAll( - () -> authInfo.id().equals(user.getId()), - () -> authInfo.name().equals(user.getName()), - () -> authInfo.email().equals(user.getEmail()) - ); + Assertions.assertThat(authUser.id()).isEqualTo(user.getId()); } } From 5ac58957770f5828a80c53156e88d416c84897b5 Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Mon, 5 Aug 2024 15:15:55 +0900 Subject: [PATCH 14/15] =?UTF-8?q?refactor:=20mock=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EC=9D=98=20sql=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/java/com/bang_ggood/AcceptanceMockTestSupport.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/backend/bang-ggood/src/test/java/com/bang_ggood/AcceptanceMockTestSupport.java b/backend/bang-ggood/src/test/java/com/bang_ggood/AcceptanceMockTestSupport.java index 0176e873..ad615ba8 100644 --- a/backend/bang-ggood/src/test/java/com/bang_ggood/AcceptanceMockTestSupport.java +++ b/backend/bang-ggood/src/test/java/com/bang_ggood/AcceptanceMockTestSupport.java @@ -7,12 +7,8 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.server.LocalServerPort; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.jdbc.Sql; import org.springframework.test.web.servlet.MockMvc; -@ActiveProfiles("test") -@Sql(scripts = {"/schema-test.sql", "/data-test.sql"}) @AutoConfigureMockMvc @SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) public abstract class AcceptanceMockTestSupport { From 3992b578866a8d0fb9df00346610916f62d670ef Mon Sep 17 00:00:00 2001 From: jinwoo22 Date: Mon, 5 Aug 2024 15:17:38 +0900 Subject: [PATCH 15/15] =?UTF-8?q?style:=20static=20=EB=B3=80=EC=88=98=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/test/java/com/bang_ggood/user/UserFixture.java | 4 ++-- .../java/com/bang_ggood/user/service/UserServiceTest.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/bang-ggood/src/test/java/com/bang_ggood/user/UserFixture.java b/backend/bang-ggood/src/test/java/com/bang_ggood/user/UserFixture.java index 65a5c11c..83e48c0e 100644 --- a/backend/bang-ggood/src/test/java/com/bang_ggood/user/UserFixture.java +++ b/backend/bang-ggood/src/test/java/com/bang_ggood/user/UserFixture.java @@ -9,11 +9,11 @@ public class UserFixture { public static final User USER1 = new User("방방이", "bang-bang@gmail.com"); public static final User USER2 = new User("빵빵이", "bbang-bbang@gmail.com"); - public static final OauthInfoResponse oauthInfoResponseUSER1 = new OauthInfoResponse("", "", + public static final OauthInfoResponse OAUTH_INFO_RESPONSE_USER1 = new OauthInfoResponse("", "", new KakaoAccountResponse(USER1.getEmail(), USER1.getName(), new ProfileResponse("", "",""))); - public static final OauthInfoResponse oauthInfoResponseUSER2 = new OauthInfoResponse("", "", + public static final OauthInfoResponse OAUTH_INFO_RESPONSE_USER2 = new OauthInfoResponse("", "", new KakaoAccountResponse(USER2.getEmail(), USER2.getName(), new ProfileResponse("", "",""))); diff --git a/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/UserServiceTest.java b/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/UserServiceTest.java index e6889a12..8fac2eb5 100644 --- a/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/UserServiceTest.java +++ b/backend/bang-ggood/src/test/java/com/bang_ggood/user/service/UserServiceTest.java @@ -14,7 +14,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import static com.bang_ggood.user.UserFixture.USER1; -import static com.bang_ggood.user.UserFixture.oauthInfoResponseUSER1; +import static com.bang_ggood.user.UserFixture.OAUTH_INFO_RESPONSE_USER1; import static org.mockito.ArgumentMatchers.any; @ExtendWith(MockitoExtension.class) @@ -36,7 +36,7 @@ class UserServiceTest extends IntegrationTestSupport { void login_signup() { // given Mockito.when(oauthClient.requestOauthInfo(any(OauthLoginRequest.class))) - .thenReturn(UserFixture.oauthInfoResponseUSER2); + .thenReturn(UserFixture.OAUTH_INFO_RESPONSE_USER2); // when String token = userService.login(oauthLoginRequest); @@ -51,7 +51,7 @@ void login() { // given userRepository.save(USER1); Mockito.when(oauthClient.requestOauthInfo(any(OauthLoginRequest.class))) - .thenReturn(oauthInfoResponseUSER1); + .thenReturn(OAUTH_INFO_RESPONSE_USER1); // when String token = userService.login(oauthLoginRequest);