From 3713b567f215ba57045e9e7e992c298b77cce51b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EC=B0=AC=EC=A4=80?= <100582352+Lunawood@users.noreply.github.com> Date: Mon, 18 Nov 2024 03:05:54 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20OpenAPI=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20Class=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20(#3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/controller/HomeController.java | 57 +++++++ .../server/controller/LoginController.java | 42 +++++ .../server/controller/RegisterController.java | 43 +++++ .../server/controller/TartarController.java | 36 ++++ .../server/controller/ToothController.java | 36 ++++ .../controller/UpdateAccessController.java | 34 ++++ .../server/controller/UserController.java | 77 +++++++++ .../com/example/server/error/CException.java | 18 ++ .../server/error/CustomErrorController.java | 66 -------- .../com/example/server/error/ErrorCode.java | 18 ++ .../example/server/error/ErrorResponse.java | 12 -- .../example/server/home/HomeController.java | 47 ------ .../com/example/server/jwt/JwtProvider.java | 71 -------- .../com/example/server/kakao/KakaoApi.java | 157 ------------------ .../example/server/kakao/KakaoController.java | 86 ---------- .../example/server/kakao/KakaoLoginDto.java | 33 ---- .../example/server/{user => model}/User.java | 27 +-- .../java/com/example/server/model/UserId.java | 15 ++ .../com/example/server/model/UserPet.java | 13 ++ .../server/repository/UserRepository.java | 54 ++++++ .../example/server/response/Successful.java | 33 ---- .../resttemplate/RestTemplateConfig.java | 13 ++ .../example/server/service/HomeService.java | 18 ++ .../server/service/InvalidTokenService.java | 52 ++++++ .../example/server/service/LoginService.java | 76 +++++++++ .../server/service/RegisterService.java | 76 +++++++++ .../server/service/UpdateAccessService.java | 55 ++++++ .../example/server/service/UserService.java | 22 +++ .../example/server/user/UserController.java | 102 ------------ .../example/server/user/UserRepository.java | 32 ---- .../com/example/server/user/UserService.java | 11 -- 31 files changed, 771 insertions(+), 661 deletions(-) create mode 100644 docker-test-server/src/main/java/com/example/server/controller/HomeController.java create mode 100644 docker-test-server/src/main/java/com/example/server/controller/LoginController.java create mode 100644 docker-test-server/src/main/java/com/example/server/controller/RegisterController.java create mode 100644 docker-test-server/src/main/java/com/example/server/controller/TartarController.java create mode 100644 docker-test-server/src/main/java/com/example/server/controller/ToothController.java create mode 100644 docker-test-server/src/main/java/com/example/server/controller/UpdateAccessController.java create mode 100644 docker-test-server/src/main/java/com/example/server/controller/UserController.java create mode 100644 docker-test-server/src/main/java/com/example/server/error/CException.java delete mode 100644 docker-test-server/src/main/java/com/example/server/error/CustomErrorController.java create mode 100644 docker-test-server/src/main/java/com/example/server/error/ErrorCode.java delete mode 100644 docker-test-server/src/main/java/com/example/server/error/ErrorResponse.java delete mode 100644 docker-test-server/src/main/java/com/example/server/home/HomeController.java delete mode 100644 docker-test-server/src/main/java/com/example/server/jwt/JwtProvider.java delete mode 100644 docker-test-server/src/main/java/com/example/server/kakao/KakaoApi.java delete mode 100644 docker-test-server/src/main/java/com/example/server/kakao/KakaoController.java delete mode 100644 docker-test-server/src/main/java/com/example/server/kakao/KakaoLoginDto.java rename docker-test-server/src/main/java/com/example/server/{user => model}/User.java (64%) create mode 100644 docker-test-server/src/main/java/com/example/server/model/UserId.java create mode 100644 docker-test-server/src/main/java/com/example/server/model/UserPet.java create mode 100644 docker-test-server/src/main/java/com/example/server/repository/UserRepository.java delete mode 100644 docker-test-server/src/main/java/com/example/server/response/Successful.java create mode 100644 docker-test-server/src/main/java/com/example/server/resttemplate/RestTemplateConfig.java create mode 100644 docker-test-server/src/main/java/com/example/server/service/HomeService.java create mode 100644 docker-test-server/src/main/java/com/example/server/service/InvalidTokenService.java create mode 100644 docker-test-server/src/main/java/com/example/server/service/LoginService.java create mode 100644 docker-test-server/src/main/java/com/example/server/service/RegisterService.java create mode 100644 docker-test-server/src/main/java/com/example/server/service/UpdateAccessService.java create mode 100644 docker-test-server/src/main/java/com/example/server/service/UserService.java delete mode 100644 docker-test-server/src/main/java/com/example/server/user/UserController.java delete mode 100644 docker-test-server/src/main/java/com/example/server/user/UserRepository.java delete mode 100644 docker-test-server/src/main/java/com/example/server/user/UserService.java diff --git a/docker-test-server/src/main/java/com/example/server/controller/HomeController.java b/docker-test-server/src/main/java/com/example/server/controller/HomeController.java new file mode 100644 index 0000000..21973f7 --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/controller/HomeController.java @@ -0,0 +1,57 @@ +package com.example.server.controller; + +import java.util.Objects; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.server.error.CException; +import com.example.server.model.User; +import com.example.server.model.UserPet; +import com.example.server.service.HomeService; +import com.example.server.service.InvalidTokenService; + +import lombok.RequiredArgsConstructor; + +@RestController // REST API 컨트롤러 선언, @Controller + @ResponseBody 결합 +@RequiredArgsConstructor // final 필드의 생성자 자동 생성 +@RequestMapping("/api/home") +public class HomeController { + private final InvalidTokenService invalidTokenService; + private final HomeService homeService; + + // 홈페이지 + // 펫이름 가져오기 + // Input : 파라미터 AccessToken + // Output(200) : 홈페이지[이미 가입한 회원] + // Output(401) : 유효하지 않은 토큰 + // Output(402) : 리프레시토큰으로 토큰 재발급 필요. + // Output(500) : 서버에러 + @GetMapping("") + public ResponseEntity home(@RequestHeader("AccessToken") String AccessToken) { + User user = null; + try { + invalidTokenService.isTokenInvalid(AccessToken); + System.out.println(AccessToken); + user = homeService.getPetInformation(AccessToken); + } catch (CException e) { + return ResponseEntity.status(e.getErrorCode().getStatus()).body(e.getErrorCode().getMessage()); + } catch (Exception e) { + return ResponseEntity.status(500).body(e.toString()); + } + + if(Objects.isNull(user)) + return ResponseEntity.status(500).body("유저 정보를 가져오지 못 했습니다."); + + System.out.print("User : "); + System.out.println(user.toString()); + + UserPet userPet = new UserPet(user.getPetName(), user.getPetWeight()); + + // access 토큰 확인 + return ResponseEntity.status(200).body(userPet); + } +} diff --git a/docker-test-server/src/main/java/com/example/server/controller/LoginController.java b/docker-test-server/src/main/java/com/example/server/controller/LoginController.java new file mode 100644 index 0000000..ff931f3 --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/controller/LoginController.java @@ -0,0 +1,42 @@ +package com.example.server.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.server.error.CException; +import com.example.server.service.LoginService; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/login") +public class LoginController { + private final LoginService loginService; + + // 로그인시 재가입 여부를 판단하는 API + // Input : AccessToken, RefreshToken, ExpiresIn, RefreshTokenExpiresIn + // Output(403) : 이미 가입한 사용자 -> 데이터 Update -> 홈페이지 + // Output(401) : 유효하지 않은 토큰 + // Output(200) : 재가입 No -> 회원가입 페이지 + @GetMapping("") + public ResponseEntity login( + @RequestHeader("AccessToken") String AccessToken, + @RequestHeader("ExpiresIn") Integer ExpiresIn, + @RequestHeader("RefreshToken") String RefreshToken, + @RequestHeader("RefreshTokenExpiresIn") Integer RefreshTokenExpiresIn + ){ + try { + loginService.isUserRejoin(AccessToken, ExpiresIn, RefreshToken, RefreshTokenExpiresIn); + } catch (CException e) { + return ResponseEntity.status(e.getErrorCode().getStatus()).body(e.getErrorCode().getMessage()); + } catch (Exception e) { + return ResponseEntity.status(500).body(e.toString()); + } + + return ResponseEntity.status(200).body("."); + } +} diff --git a/docker-test-server/src/main/java/com/example/server/controller/RegisterController.java b/docker-test-server/src/main/java/com/example/server/controller/RegisterController.java new file mode 100644 index 0000000..6a165dc --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/controller/RegisterController.java @@ -0,0 +1,43 @@ +package com.example.server.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.server.error.CException; +import com.example.server.service.RegisterService; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/register") +public class RegisterController { + private final RegisterService registerService; + + // 회원가입이 끝나면 저장하기 위한 API + // Input : AccessToken, ExpiresI, RefreshToken, RefreshTokenExpiresIn, PetName, PetWeight + // Output : status(200, 401, 500) + @PostMapping("") + public ResponseEntity register( + @RequestHeader("AccessToken") String AccessToken, + @RequestHeader("ExpiresIn") Integer ExpiresIn, + @RequestHeader("RefreshToken") String RefreshToken, + @RequestHeader("RefreshTokenExpiresIn") Integer RefreshTokenExpiresIn, + @RequestHeader("PetName") String PetName, + @RequestHeader("PetWeight") Integer PetWeight + ) { + Integer status = 200; + try { + registerService.isUserRejoin(AccessToken, ExpiresIn, RefreshToken, RefreshTokenExpiresIn, PetName, PetWeight); + } catch (CException e) { + return ResponseEntity.status(e.getErrorCode().getStatus()).body(e.getErrorCode().getMessage()); + } catch (Exception e) { + return ResponseEntity.status(500).body(e.toString()); + } + + return ResponseEntity.status(status).body("."); + } +} diff --git a/docker-test-server/src/main/java/com/example/server/controller/TartarController.java b/docker-test-server/src/main/java/com/example/server/controller/TartarController.java new file mode 100644 index 0000000..86fdbbc --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/controller/TartarController.java @@ -0,0 +1,36 @@ +package com.example.server.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.server.error.CException; +import com.example.server.service.InvalidTokenService; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/tartar") +public class TartarController { + private final InvalidTokenService invalidTokenService; + + // Default : + // 1. Token이 유효한지 확인. + + // 사용자가 애완견 이빨을 찍으면 + @GetMapping("") + public ResponseEntity tartar(@RequestHeader("AccessToken") String AccessToken) { + try { + invalidTokenService.isTokenInvalid(AccessToken); + } catch (CException e) { + ResponseEntity.status(e.getErrorCode().getStatus()).body(e.getErrorCode().getMessage()); + } catch (Exception e) { + ResponseEntity.status(500).body(e.toString()); + } + + return ResponseEntity.status(200).body("null"); + } +} diff --git a/docker-test-server/src/main/java/com/example/server/controller/ToothController.java b/docker-test-server/src/main/java/com/example/server/controller/ToothController.java new file mode 100644 index 0000000..91e0fda --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/controller/ToothController.java @@ -0,0 +1,36 @@ +package com.example.server.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.server.error.CException; +import com.example.server.service.InvalidTokenService; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/tooth") +public class ToothController { + // 양치질이 끝나면 또는 주기적으로 Front에서 데이터를 보내면 + // TimeStream에 저장해주는 역할 API. + + private final InvalidTokenService invalidTokenService; + + // 사용자가 애완견 이빨을 찍으면 + @GetMapping("") + public ResponseEntity tooth(@RequestHeader("AccessToken") String AccessToken) { + try { + invalidTokenService.isTokenInvalid(AccessToken); + } catch (CException e) { + ResponseEntity.status(e.getErrorCode().getStatus()).body(e.getErrorCode().getMessage()); + } catch (Exception e) { + ResponseEntity.status(500).body(e.toString()); + } + + return ResponseEntity.status(200).body("null"); + } +} diff --git a/docker-test-server/src/main/java/com/example/server/controller/UpdateAccessController.java b/docker-test-server/src/main/java/com/example/server/controller/UpdateAccessController.java new file mode 100644 index 0000000..903bd29 --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/controller/UpdateAccessController.java @@ -0,0 +1,34 @@ +package com.example.server.controller; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.server.error.CException; +import com.example.server.service.UpdateAccessService; + +import lombok.RequiredArgsConstructor; + +@RestController // REST API 컨트롤러 선언, @Controller + @ResponseBody 결합 +@RequiredArgsConstructor // final 필드의 생성자 자동 생성 +@RequestMapping("/api/update") +public class UpdateAccessController { + private UpdateAccessService updateAccessService; + + // AccessToken을 새로 발급 받았을때 사용하는 API. + // 서버에 새로운 AccessToken을 제공. + @GetMapping("/accesstoken") + public ResponseEntity updateAccessToken(@RequestHeader("AccessToken") String AccessToken, + @RequestHeader("ExpiresIn") Integer ExpiresIn) { + try { + updateAccessService.updateAccessTokenById(AccessToken, ExpiresIn); + } catch(CException e) { + ResponseEntity.status(e.getErrorCode().getStatus()).body(e.getErrorCode().getMessage()); + } catch(Exception e) { + ResponseEntity.status(500).body(e.toString()); + } + return ResponseEntity.status(null).body(null); + } +} diff --git a/docker-test-server/src/main/java/com/example/server/controller/UserController.java b/docker-test-server/src/main/java/com/example/server/controller/UserController.java new file mode 100644 index 0000000..3ce2e8a --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/controller/UserController.java @@ -0,0 +1,77 @@ +package com.example.server.controller; + +import java.util.Objects; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.example.server.error.CException; +import com.example.server.model.User; +import com.example.server.model.UserPet; +import com.example.server.service.InvalidTokenService; +import com.example.server.service.UserService; + +import lombok.RequiredArgsConstructor; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/user") +public class UserController { + private InvalidTokenService invalidTokenService; + private UserService userService; + + @GetMapping("test") + public ResponseEntity test() { + return ResponseEntity.ok().body("."); + } + + // 마이페이지 + // 마이페이지를 보여주는 API + // Input : AccessToken + // Output : User + @GetMapping("/getinfo") + public ResponseEntity mypageGetinfo(@RequestHeader("AccessToken") String AccessToken) { + User user = null; + try { + invalidTokenService.isTokenInvalid(AccessToken); + user = userService.getPetInformation(AccessToken); + } catch (CException e) { + ResponseEntity.status(e.getErrorCode().getStatus()).body(e.getErrorCode().getMessage()); + } catch (Exception e) { + ResponseEntity.status(500).body(e.toString()); + } + + if(Objects.isNull(user)) + ResponseEntity.status(500).body("유저 정보를 가져오지 못 했습니다."); + + UserPet userPet = new UserPet(user.getPetName(), user.getPetWeight()); + + return ResponseEntity.status(200).body(userPet); + } + + // 마이페이지 수정 + // 마이페이지를 수정하는 API + // Input : AccessToken, PetName, PetWeight + // Output : status(200, 401, 500) + @PutMapping("/setinfo") + public ResponseEntity mypageGetinfo( + @RequestHeader("AccessToken") String AccessToken, + @RequestHeader("PetName") String PetName, + @RequestHeader("PetWeight") Integer PetWeight + ) { + try { + invalidTokenService.isTokenInvalid(AccessToken); + userService.setPetInformation(AccessToken, PetName, PetWeight); + } catch (CException e) { + ResponseEntity.status(e.getErrorCode().getStatus()).body(e.getErrorCode().getMessage()); + } catch (Exception e) { + ResponseEntity.status(500).body(e.toString()); + } + + return ResponseEntity.ok().body("."); + } +} \ No newline at end of file diff --git a/docker-test-server/src/main/java/com/example/server/error/CException.java b/docker-test-server/src/main/java/com/example/server/error/CException.java new file mode 100644 index 0000000..6d22e7f --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/error/CException.java @@ -0,0 +1,18 @@ +package com.example.server.error; + +import lombok.Getter; + +@Getter +public class CException extends RuntimeException { + private final ErrorCode errorCode; + + public CException(ErrorCode errorCode) { + super(errorCode.getMessage()); + this.errorCode = errorCode; + } + + public CException(ErrorCode errorCode, String message) { + super(message); + this.errorCode = errorCode; + } +} diff --git a/docker-test-server/src/main/java/com/example/server/error/CustomErrorController.java b/docker-test-server/src/main/java/com/example/server/error/CustomErrorController.java deleted file mode 100644 index 109dae3..0000000 --- a/docker-test-server/src/main/java/com/example/server/error/CustomErrorController.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.example.server.error; - -import org.springframework.boot.web.servlet.error.ErrorController; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseBody; - -import javax.servlet.RequestDispatcher; -import javax.servlet.http.HttpServletRequest; - -@Controller -public class CustomErrorController implements ErrorController { - @RequestMapping("/error") - @ResponseBody - public ResponseEntity handleError(HttpServletRequest request) { - Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE); - ErrorResponse errorResponse; - - if (status != null) { - int statusCode = Integer.valueOf(status.toString()); - - switch (statusCode) { - case 500: - errorResponse = ErrorResponse.builder() - .status(500) - .message("서버 내부 오류") - .code("INTERNAL_SERVER_ERROR") - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); - - case 401: - errorResponse = ErrorResponse.builder() - .status(401) - .message("유효하지 않은 토큰") - .code("UNAUTHORIZED") - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.UNAUTHORIZED); - - case 400: - errorResponse = ErrorResponse.builder() - .status(400) - .message("잘못된 액세스토큰 형식") - .code("WRONG_ACCESSTOKEN") - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.PAYMENT_REQUIRED); - - default: - errorResponse = ErrorResponse.builder() - .status(statusCode) - .message("알 수 없는 오류가 발생했습니다") - .code("UNKNOWN_ERROR") - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.valueOf(statusCode)); - } - } - - errorResponse = ErrorResponse.builder() - .status(500) - .message("알 수 없는 오류가 발생했습니다") - .code("UNKNOWN_ERROR") - .build(); - return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); - } -} \ No newline at end of file diff --git a/docker-test-server/src/main/java/com/example/server/error/ErrorCode.java b/docker-test-server/src/main/java/com/example/server/error/ErrorCode.java new file mode 100644 index 0000000..92af3c4 --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/error/ErrorCode.java @@ -0,0 +1,18 @@ +package com.example.server.error; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum ErrorCode { + SUCCESS(0, "성공"), + INVALID_TOKEN(401, "유효하지 않은 토큰입니다."), + REESSUIE_TOKEN(402, "토큰을 재발급 받아야합니다."), + ALREADY_REGISTER_USER(403, "이미 가입한 유저입니다."), + INTERNAL_SERVER_ERROR(500, "서버 오류가 발생했습니다."), + DATABASE_ERROR(501, "데이터베이스 오류가 발생했습니다"); + + private final int status; + private final String message; +} diff --git a/docker-test-server/src/main/java/com/example/server/error/ErrorResponse.java b/docker-test-server/src/main/java/com/example/server/error/ErrorResponse.java deleted file mode 100644 index 09503ff..0000000 --- a/docker-test-server/src/main/java/com/example/server/error/ErrorResponse.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.example.server.error; - -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder -public class ErrorResponse { - private final int status; - private final String message; - private final String code; -} \ No newline at end of file diff --git a/docker-test-server/src/main/java/com/example/server/home/HomeController.java b/docker-test-server/src/main/java/com/example/server/home/HomeController.java deleted file mode 100644 index 7a31c35..0000000 --- a/docker-test-server/src/main/java/com/example/server/home/HomeController.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.example.server.home; - -import org.springframework.web.bind.annotation.RestController; - -import com.example.server.kakao.KakaoApi; - -import java.util.HashMap; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestMapping; - -import lombok.RequiredArgsConstructor; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/home") -public class HomeController { - private final KakaoApi kakaoApi = new KakaoApi(); - - // 홈 화면 - // 1. 토큰 확인. - // 2. 토큰 만료시 재발급. - // 3. 리프레시 토큰 또한 만료시 로그인 화면 이동. - @GetMapping("") - public ResponseEntity home(@RequestHeader("accesstoken") String accesstoken, - @RequestHeader("refreshtoken") String refreshtoken) - throws Exception { - - HashMap map = kakaoApi.validateToken(accesstoken, refreshtoken); - - if (map.get("status").equals("false")) { - return ResponseEntity - .status(401) - .header("Location", "/api/user/login") - .build(); - } - - return ResponseEntity - .status(200) - .header("AccessToken", map.get("accessTokenJWT")) - .header("RefreshToken", map.get("refreshTokenJWT")) - .header("Location", "") - .build(); - } -} diff --git a/docker-test-server/src/main/java/com/example/server/jwt/JwtProvider.java b/docker-test-server/src/main/java/com/example/server/jwt/JwtProvider.java deleted file mode 100644 index 0467688..0000000 --- a/docker-test-server/src/main/java/com/example/server/jwt/JwtProvider.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.example.server.jwt; - -import io.jsonwebtoken.*; -import io.jsonwebtoken.security.*; -import org.springframework.stereotype.Component; -import org.springframework.beans.factory.annotation.Value; - -import java.nio.charset.StandardCharsets; -import java.security.Key; - -import javax.crypto.SecretKey; - -@Component -public class JwtProvider { - @Value("${key.jwt-secret-key}") - private String secretKey; - - - - private Key getSigningKey() { - byte[] keyBytes = this.secretKey.getBytes(StandardCharsets.UTF_8); - return Keys.hmacShaKeyFor(keyBytes); - } - - // JWT 토큰에서 모든 클레임 추출 - private Claims getAllClaimsFromToken(String token) { - SecretKey key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)); - return Jwts.parserBuilder() - .setSigningKey(key) - .build() - .parseClaimsJws(token) - .getBody(); - } - - // 문자열을 JWT로 암호화. - public String createJWTToken(String payload) { - return Jwts.builder() - .setSubject("JWT") // 예시로 "JWT" 사용 - .claim("data", payload) - .signWith(getSigningKey()) - .compact(); - } - - // JWT를 다시 문자열로 비암호화 - public String decodeJWTToken(String jwtToken) { - Claims claims; - try { - claims = getAllClaimsFromToken(jwtToken); - } catch (Exception e) { - return ""; - } - return claims.get("data", String.class); - } - - // JWT 토큰을 검증하고 Kakao토큰 추출. - public String validateAndGetPayload(String token) { - Claims claims; - - try { - claims = Jwts.parserBuilder() - .setSigningKey(getSigningKey()) - .build() - .parseClaimsJws(token) - .getBody(); - } catch (Exception e) { - return null; - } - - return claims.get("data", String.class); - } -} \ No newline at end of file diff --git a/docker-test-server/src/main/java/com/example/server/kakao/KakaoApi.java b/docker-test-server/src/main/java/com/example/server/kakao/KakaoApi.java deleted file mode 100644 index f9c6cf8..0000000 --- a/docker-test-server/src/main/java/com/example/server/kakao/KakaoApi.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.example.server.kakao; - -import java.util.HashMap; - -import org.json.JSONObject; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; - -import com.example.server.jwt.JwtProvider; -import com.example.server.user.UserRepository; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; - -public class KakaoApi { - private RestTemplate restTemplate = new RestTemplate(); - @Value("{key.kakao-api-key}") - private String CLIENT_ID; - private UserRepository userRepository; - private ObjectMapper objectMapper = new ObjectMapper(); - private JwtProvider jwtProvider = new JwtProvider(); - - // 인가 코드를 받아서 accessToken을 반환 - public KakaoLoginDto getAccessToken(String code, String CLIENT_ID, String REDIRECT_URL) throws Exception { - final String apiUrl = "https://kauth.kakao.com/oauth/token"; - - MultiValueMap requestData = new LinkedMultiValueMap<>(); - requestData.add("grant_type", "authorization_code"); - requestData.add("client_id", CLIENT_ID); - requestData.add("redirect_uri", REDIRECT_URL); - requestData.add("code", code); - - ResponseEntity responseEntity = restTemplate.postForEntity(apiUrl, requestData, - String.class); - - // 에러 발생 - if (responseEntity.getStatusCode() != HttpStatus.OK) - throw new Exception(responseEntity.getStatusCode().toString()); - - JsonNode jsonNode = objectMapper.readTree(responseEntity.getBody()); - - KakaoLoginDto kakaoLoginDto = new KakaoLoginDto(); - kakaoLoginDto.setAccessToken((String) jsonNode.get("access_token").asText()); - kakaoLoginDto.setRefreshToken((String) jsonNode.get("refresh_token").asText()); - - return kakaoLoginDto; - } - - // accessToken을 받아서 사용자 정보(Email)로 반환 - public String getUserInfo(String accessToken) throws Exception { - String userInfoUrl = "https://kapi.kakao.com/v2/user/me"; - - HttpHeaders headers = new HttpHeaders(); - headers.setBearerAuth(accessToken); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - - HttpEntity requestEntity = new HttpEntity<>(headers); - - ResponseEntity responseEntity = restTemplate.exchange( - userInfoUrl, - HttpMethod.GET, - requestEntity, - String.class); - - if (responseEntity.getStatusCode() != HttpStatus.OK) - throw new Exception("사용자 정보를 받아오는 중 에러."); - - JSONObject jsonObject = new JSONObject(responseEntity.getBody()); - JSONObject kakaoAccount = jsonObject.getJSONObject("kakao_account"); - - if (!kakaoAccount.has("email")) { // 로그인 화면으로 이동. - return ""; - } - - return kakaoAccount.get("email").toString(); - } - - // accessToken을 받아서 로그아웃 시키는 메서드 - public void kakaoLogout(String accessToken) throws Exception { - String logoutUrl = "https://kapi.kakao.com/v1/user/logout"; - - HttpHeaders headers = new HttpHeaders(); - headers.setBearerAuth(accessToken); // Bearer 인증 방식으로 액세스 토큰 설정 - - HttpEntity requestEntity = new HttpEntity<>(headers); - - ResponseEntity responseEntity = restTemplate.postForEntity( - logoutUrl, - requestEntity, - String.class); - - if (responseEntity.getStatusCode() != HttpStatus.OK) { - throw new RuntimeException("카카오 로그아웃 실패: " + responseEntity.getStatusCode()); - } - } - - // kakao 토큰 유효한지 확인하는 함수. - // 1-1. 만료되지X 통과 (True) - // 1-2. 만료시 refreshToken 만료체크 - // 2-1. refreshToken 만료되지X accessToken 재발급 (True) - // 2-2. refreshToken 만료시 로그인 화면 이동 (False) - public HashMap validateToken(String accessTokenJWT, String refreshTokenJWT) throws Exception { - HashMap map = new HashMap(); - - HttpHeaders headers = new HttpHeaders(); - headers.setBearerAuth(jwtProvider.decodeJWTToken(accessTokenJWT)); // Bearer 토큰 설정 - - HttpEntity entity = new HttpEntity<>(headers); - - ResponseEntity responseEntity = restTemplate.exchange( - "https://kapi.kakao.com/v1/user/access_token_info", - HttpMethod.GET, - entity, - String.class); - - if (responseEntity.getStatusCode() == HttpStatus.OK) { - map.put("status", "true"); - map.put("accessTokenJWT", accessTokenJWT); - map.put("refreshTokenJWT", refreshTokenJWT); - return map; - } - - // RefreshToken 확인 - String refreshToken = jwtProvider.decodeJWTToken(refreshTokenJWT); - String userInfoUrl = "https://kauth.kakao.com/oauth/token"; - - MultiValueMap requestData = new LinkedMultiValueMap<>(); - requestData.add("grant_type", "refresh_token"); - requestData.add("client_id", CLIENT_ID); - requestData.add("refresh_token", refreshToken); - - responseEntity = restTemplate.postForEntity(userInfoUrl, - requestData, String.class); - - if (responseEntity.getStatusCode() == HttpStatus.OK) { - JsonNode jsonNode = objectMapper.readTree(responseEntity.getBody()); - userRepository.updateUserByRefreshToken(jwtProvider.decodeJWTToken(refreshTokenJWT), - jwtProvider.createJWTToken((String) jsonNode.get("access_token").asText())); - map.put("status", "true"); - map.put("accessTokenJWT", jwtProvider.createJWTToken((String) jsonNode.get("access_token").asText())); - map.put("refreshTokenJWT", refreshTokenJWT); - return map; - } - - map.put("status", "false"); - map.put("accessTokenJWT", ""); - map.put("refreshTokenJWT", ""); - return map; - } -} \ No newline at end of file diff --git a/docker-test-server/src/main/java/com/example/server/kakao/KakaoController.java b/docker-test-server/src/main/java/com/example/server/kakao/KakaoController.java deleted file mode 100644 index 3ce73f3..0000000 --- a/docker-test-server/src/main/java/com/example/server/kakao/KakaoController.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.example.server.kakao; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import com.example.server.jwt.JwtProvider; -import com.example.server.user.User; -import com.example.server.user.UserRepository; - -import org.springframework.web.bind.annotation.RequestMapping; - -import lombok.RequiredArgsConstructor; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/kakao") -public class KakaoController { - private final KakaoApi kakaoApi = new KakaoApi(); - private final String REDIRECT_URL = "http://localhost:8080/api/kakao/login/oauth2/code"; - @Value("{key.kakao-api-key}") - private final String CLIENT_ID; - private final UserRepository userRepository; - private final JwtProvider jwtProvider; - - // 1. 코드 발급 받기 - @GetMapping("/getcode") - public String getcode() throws Exception { - String apiUrl = "https://kauth.kakao.com/oauth/authorize" + - "?client_id=" + CLIENT_ID + - "&redirect_uri=" + REDIRECT_URL + - "&response_type=code" + - "&scope=account_email" + - "&prompt=select_account consent"; - - return "redirect:" + apiUrl; - } - - // Kakao 버튼을 클릭시 발생. - @GetMapping("/login/oauth2/code") - public ResponseEntity kakaoLogin(@RequestParam("code") String code) throws Exception { - // 1. 코드 발급 받기 (위에 작성) - - // 2. 코드로 액세스토큰과 리프레시토큰 받기 - KakaoLoginDto kakaoLoginDto = kakaoApi.getAccessToken(code, CLIENT_ID, REDIRECT_URL); - - // 3. 사용자 정보 받기 (email) - String email = kakaoApi.getUserInfo(kakaoLoginDto.getAccessToken()); - - // 4. 이메일 형식이 다르면 login 화면으로 이동 - if (email.equals("")) { - return ResponseEntity - .status(200) - .header("Location", "/api/user/login") - .build(); - } - - // 5. 데이터베이스에서 확인, 있으면 Home으로 이동 & 갱신된 Token 저장 : 없으면 펫 설정으로 이동 & 새 Token 저장. - User user = userRepository.findByEmail(email); - - if (user == null) { - User newUser = new User(email, "", 0, kakaoLoginDto.getAccessToken(), kakaoLoginDto.getRefreshToken()); - userRepository.save(newUser); - return ResponseEntity - .status(200) - .header("Email", email) - .header("AccessToken", jwtProvider.createJWTToken(kakaoLoginDto.getAccessToken())) - .header("RefreshToken", jwtProvider.createJWTToken(kakaoLoginDto.getRefreshToken())) - .header("Location", "/api/user/petsetting") - .build(); - } - - String accessTokenJWT = jwtProvider.createJWTToken(user.getAccessToken()); - String refreshTokenJWT = jwtProvider.createJWTToken(user.getRefreshToken()); - - return ResponseEntity - .status(200) - .header("Email", email) - .header("AccessToken", accessTokenJWT) - .header("RefreshToken", refreshTokenJWT) - .header("Location", "/api/home") - .build(); - } -} diff --git a/docker-test-server/src/main/java/com/example/server/kakao/KakaoLoginDto.java b/docker-test-server/src/main/java/com/example/server/kakao/KakaoLoginDto.java deleted file mode 100644 index 02ed89a..0000000 --- a/docker-test-server/src/main/java/com/example/server/kakao/KakaoLoginDto.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.example.server.kakao; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@AllArgsConstructor -@NoArgsConstructor -@Builder -@Getter -@Setter -public class KakaoLoginDto { - public String accessToken; - public String refreshToken; - - public void setAccessToken(String accessToken) { - this.accessToken = accessToken; - } - - public void setRefreshToken(String refreshToken) { - this.refreshToken = refreshToken; - } - - public String getAccessToken() { - return accessToken; - } - - public String getRefreshToken() { - return refreshToken; - } -} \ No newline at end of file diff --git a/docker-test-server/src/main/java/com/example/server/user/User.java b/docker-test-server/src/main/java/com/example/server/model/User.java similarity index 64% rename from docker-test-server/src/main/java/com/example/server/user/User.java rename to docker-test-server/src/main/java/com/example/server/model/User.java index 8fcc774..6deb484 100644 --- a/docker-test-server/src/main/java/com/example/server/user/User.java +++ b/docker-test-server/src/main/java/com/example/server/model/User.java @@ -1,4 +1,6 @@ -package com.example.server.user; +package com.example.server.model; + +import java.time.LocalDateTime; import javax.persistence.Column; import javax.persistence.Entity; @@ -6,31 +8,34 @@ import javax.persistence.Table; import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; +import lombok.Data; import lombok.NoArgsConstructor; -import lombok.Setter; -@Getter -@Setter +@Data @NoArgsConstructor // 기본 생성자 @AllArgsConstructor // 모든 필드를 파라미터로 받는 생성자 -@Builder // Builder 패턴 -@Table(name = "user_tb") @Entity +@Table(name = "user_tb") public class User { @Id - private String email; + @Column(name = "id", columnDefinition = "BIGINT") + private Long id; @Column(name = "pet_name") private String petName; @Column(name = "pet_weight") private Integer petWeight; - + @Column(name = "access_token") private String accessToken; - + + @Column(name = "expires_in") + private LocalDateTime expiresIn; + @Column(name = "refresh_token") private String refreshToken; + + @Column(name = "refresh_token_expires_in") + private LocalDateTime refreshTokenExpiresIn; } \ No newline at end of file diff --git a/docker-test-server/src/main/java/com/example/server/model/UserId.java b/docker-test-server/src/main/java/com/example/server/model/UserId.java new file mode 100644 index 0000000..3764936 --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/model/UserId.java @@ -0,0 +1,15 @@ +package com.example.server.model; + +import javax.persistence.Id; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UserId { + @Id + private Long id; +} diff --git a/docker-test-server/src/main/java/com/example/server/model/UserPet.java b/docker-test-server/src/main/java/com/example/server/model/UserPet.java new file mode 100644 index 0000000..219b5a1 --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/model/UserPet.java @@ -0,0 +1,13 @@ +package com.example.server.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UserPet { + private String petName; + private Integer petWeight; +} diff --git a/docker-test-server/src/main/java/com/example/server/repository/UserRepository.java b/docker-test-server/src/main/java/com/example/server/repository/UserRepository.java new file mode 100644 index 0000000..a68fe02 --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/repository/UserRepository.java @@ -0,0 +1,54 @@ +package com.example.server.repository; + +import java.time.LocalDateTime; + + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import com.example.server.model.User; + +@Repository +public interface UserRepository extends JpaRepository { + @Query("SELECT CASE WHEN COUNT(u) > 0 THEN true ELSE false END FROM User u WHERE u.accessToken = :accessToken") + boolean existsByAccessToken(@Param("accessToken") String accessToken); + + @Query("SELECT u.refreshToken FROM User u WHERE u.accessToken = :accessToken") + String getRefreshTokenByAccessToken(@Param("accessToken") String accessToken); + + @Query("SELECT CASE WHEN COUNT(u) > 0 THEN true ELSE false END FROM User u WHERE u.accessToken = :accessToken AND u.expiresIn < CURRENT_TIMESTAMP") + boolean isAccessTokenInvalid(@Param("accessToken") String accessToken); + + @Query("SELECT CASE WHEN COUNT(u) > 0 THEN true ELSE false END FROM User u WHERE u.refreshToken = :refreshToken AND u.refreshTokenExpiresIn < CURRENT_TIMESTAMP") + boolean isRefreshTokenInvalid(@Param("refreshToken") String refreshToken); + + @Query("SELECT CASE WHEN COUNT(u) > 0 THEN true ELSE false END FROM User u WHERE u.id = :id") + boolean existsById(@Param("id") Long id); + + @Modifying + @Transactional + @Query(value = "INSERT INTO user_tb (id, pet_name, pet_weight, access_token, expires_in, refresh_token, refresh_token_expires_in) VALUES (:#{#user.id}, :#{#user.petName}, :#{#user.petWeight}, :#{#user.accessToken}, :#{#user.expiresIn}, :#{#user.refreshToken}, :#{#user.refreshTokenExpiresIn})", nativeQuery = true) + void insertUserInformation(@Param("user") User user); + + @Modifying + @Transactional + @Query(value = "UPDATE user_tb u SET u.access_token=:accessToken, u.expires_in=:expiresIn, u.refresh_token=:refreshToken, u.refresh_token_expires_in=:refreshTokenExpiresIn WHERE u.id = :id", nativeQuery = true) + void updateUserTokenInformationById(@Param("id") Long id, @Param("accessToken") String accessToken, @Param("expiresIn") LocalDateTime expiresIn, @Param("refreshToken") String refreshToken, @Param("refreshTokenExpiresIn") LocalDateTime refreshTokenExpiresIn); + + @Query("SELECT u FROM User u WHERE u.accessToken = :accessToken") + User getUserInformationByAccessToken(@Param("accessToken") String accessToken); + + @Modifying + @Transactional + @Query(value = "UPDATE user_tb u SET u.pet_name=:petName, u.pet_weight=:petWeight WHERE u.access_token=:accessToken", nativeQuery = true) + void setUserPetInformationByAccessToken(@Param("accessToken") String accessToken, @Param("petName") String petName, @Param("petWeight") Integer petWeight); + + @Modifying + @Transactional + @Query(value = "UPDATE user_tb u SET u.access_token=:accessToken, u.expires_in=:expiresIn WHERE u.id = :id", nativeQuery = true) + void updateAccessTokenById(@Param("id") Long id, @Param("accessToken") String accessToken, @Param("expiresIn") LocalDateTime expiresIn); +} \ No newline at end of file diff --git a/docker-test-server/src/main/java/com/example/server/response/Successful.java b/docker-test-server/src/main/java/com/example/server/response/Successful.java deleted file mode 100644 index b5b2bf2..0000000 --- a/docker-test-server/src/main/java/com/example/server/response/Successful.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.example.server.response; - -import org.json.JSONObject; - -public class Successful { - private Integer status = 200; - private String message; - private JSONObject data; - - public void setStatus(Integer status) { - this.status = status; - } - - public void setMessage(String message) { - this.message = message; - } - - public void setJsonobject(JSONObject data) { - this.data = data; - } - - public Integer getStatus() { - return status; - } - - public String getMessage() { - return message; - } - - public JSONObject getJsonobject() { - return data; - } -} diff --git a/docker-test-server/src/main/java/com/example/server/resttemplate/RestTemplateConfig.java b/docker-test-server/src/main/java/com/example/server/resttemplate/RestTemplateConfig.java new file mode 100644 index 0000000..802bb3e --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/resttemplate/RestTemplateConfig.java @@ -0,0 +1,13 @@ +package com.example.server.resttemplate; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} \ No newline at end of file diff --git a/docker-test-server/src/main/java/com/example/server/service/HomeService.java b/docker-test-server/src/main/java/com/example/server/service/HomeService.java new file mode 100644 index 0000000..cf9eb60 --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/service/HomeService.java @@ -0,0 +1,18 @@ +package com.example.server.service; + +import org.springframework.stereotype.Service; + +import com.example.server.model.User; +import com.example.server.repository.UserRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class HomeService { + private final UserRepository userRepository; + + public User getPetInformation(String AccessToken) throws Exception{ + return userRepository.getUserInformationByAccessToken(AccessToken); + } +} \ No newline at end of file diff --git a/docker-test-server/src/main/java/com/example/server/service/InvalidTokenService.java b/docker-test-server/src/main/java/com/example/server/service/InvalidTokenService.java new file mode 100644 index 0000000..ca2b9af --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/service/InvalidTokenService.java @@ -0,0 +1,52 @@ +package com.example.server.service; + +import org.springframework.stereotype.Service; + +import com.example.server.error.CException; +import com.example.server.error.ErrorCode; +import com.example.server.repository.UserRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor // final 필드의 생성자 자동 생성 +public class InvalidTokenService { + private final UserRepository userRepository; + + /* Input : Token, Output : Boolean + * 1. AccessToken 유(2)/무(401) + * 2. AccessToken Expire Yes(2-1)/No(3) + * 2-1. RefreshToken 유(2-2)/무(401) + * 2-2. RefreshToken Expire Yes(401)/No(2-3) + * 2-3. AccessToken 재발급(402) + * 3. return + * Tip. Token 유/무는 데이터베이스로 Check한다. + */ + public void isTokenInvalid(String AccessToken) throws Exception { + // 1. AccessToken 유(2)/무(401) + boolean flagAccessToken = userRepository.existsByAccessToken(AccessToken); + if (flagAccessToken == false) + throw new CException(ErrorCode.INVALID_TOKEN); + + // 2. AccessToken Expire Yes(2-1)/No(3) + boolean flagAccessTokenExpire = userRepository.isAccessTokenInvalid(AccessToken); + if (flagAccessTokenExpire == false) { + return; + } + + // 2-1. RefreshToken 유(2-2)/무(401) + String RefreshToken = userRepository.getRefreshTokenByAccessToken(AccessToken); + if(RefreshToken == null) { + throw new CException(ErrorCode.INVALID_TOKEN); + } + + // 2-2. RefreshToken Expire Yes(401)/No(2-3) + boolean flagRefreshTokenExpire = userRepository.isRefreshTokenInvalid(RefreshToken); + if(flagRefreshTokenExpire == true) { + throw new CException(ErrorCode.INVALID_TOKEN); + } + + // 2-3. AccessToken 재발급(402) + throw new CException(ErrorCode.REESSUIE_TOKEN); + } +} diff --git a/docker-test-server/src/main/java/com/example/server/service/LoginService.java b/docker-test-server/src/main/java/com/example/server/service/LoginService.java new file mode 100644 index 0000000..3e93319 --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/service/LoginService.java @@ -0,0 +1,76 @@ +package com.example.server.service; + +import java.time.LocalDateTime; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import com.example.server.error.CException; +import com.example.server.error.ErrorCode; +import com.example.server.model.UserId; +import com.example.server.repository.UserRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class LoginService { + private String GET_USER_INFORMATION_URL="https://kapi.kakao.com/v2/user/me"; + private final RestTemplate restTemplate; + private final UserRepository userRepository; + + // 0. 유효하지 않은 토큰이면 (401) + // 1. AccessToken으로 UserID가져오기 + // 2. id로 데이터베이스에 id값이 있는지 확인 + // 3-1. 있으면 이미 회원가입한 사용자 (403), Token update할 것. + // 3-2. 없으면 회원가입으로 이동 (200) + + // Input : AccessToken, Output : status(200, 401, 403) + // Output(403) : 이미 재가입 Yes -> 데이터 Update -> 홈페이지 + // Output(401) : 유효하지 않은 토큰 + // Output(200) : 재가입 No -> 회원가입 페이지 + public void isUserRejoin(String AccessToken, Integer ExpiresIn, String RefreshToken, Integer RefreshTokenExpiresIn) throws Exception { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.add("Authorization", "Bearer " + AccessToken); + headers.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); + + HttpEntity> httpEntity = new HttpEntity<>(headers); + + ResponseEntity responseEntity; + try { + responseEntity = restTemplate.postForEntity( + GET_USER_INFORMATION_URL, + httpEntity, + UserId.class + ); + } catch(Exception e) { + throw new CException(ErrorCode.INVALID_TOKEN); + } + + UserId userId = responseEntity.getBody(); + if(userId == null) { + throw new CException(ErrorCode.INTERNAL_SERVER_ERROR); + } + + boolean isIdFlag = userRepository.existsById(userId.getId()); + + // 이미 회원가입한 사용자(403오류), Token update후 홈페이지로 이동. + if(isIdFlag == true) { + LocalDateTime ExpiresInTime = LocalDateTime.now().plusSeconds(ExpiresIn); + System.out.println(ExpiresIn); + System.out.println(LocalDateTime.now()); + System.out.println(ExpiresInTime); + LocalDateTime RefreshTokenExpiresInTime = LocalDateTime.now().plusSeconds(RefreshTokenExpiresIn); + + userRepository.updateUserTokenInformationById(userId.getId(), AccessToken, ExpiresInTime, AccessToken, RefreshTokenExpiresInTime); + + throw new CException(ErrorCode.ALREADY_REGISTER_USER); + } + } +} diff --git a/docker-test-server/src/main/java/com/example/server/service/RegisterService.java b/docker-test-server/src/main/java/com/example/server/service/RegisterService.java new file mode 100644 index 0000000..4770df9 --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/service/RegisterService.java @@ -0,0 +1,76 @@ +package com.example.server.service; + +import java.time.LocalDateTime; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import com.example.server.error.CException; +import com.example.server.error.ErrorCode; +import com.example.server.model.User; +import com.example.server.model.UserId; +import com.example.server.repository.UserRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class RegisterService { + private String GET_USER_INFORMATION_URL="https://kapi.kakao.com/v2/user/me"; + private final RestTemplate restTemplate; + private final UserRepository userRepository; + + // AccessToken으로 UserID가져오기 + // Input : AccessToken, Output : status(200, 401, 500) + public void isUserRejoin( + String AccessToken, + Integer ExpiresIn, + String RefreshToken, + Integer RefreshTokenExpiresIn, + String PetName, + Integer PetWeight) throws Exception { + // ID값 가져오기 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.add("Authorization", "Bearer " + AccessToken); + headers.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); + + HttpEntity> httpEntity = new HttpEntity<>(headers); + + ResponseEntity responseEntity; + try { + responseEntity = restTemplate.postForEntity( + GET_USER_INFORMATION_URL, + httpEntity, + UserId.class + ); + } catch(Exception e) { + throw new CException(ErrorCode.INVALID_TOKEN); + } + + UserId userId = responseEntity.getBody(); + if(userId == null) { + throw new CException(ErrorCode.INTERNAL_SERVER_ERROR); + } + + // 시간 변형 + LocalDateTime ExpiresInTime = LocalDateTime.now().plusSeconds(ExpiresIn); + LocalDateTime RefreshTokenExpiresInTime = LocalDateTime.now().plusSeconds(RefreshTokenExpiresIn); + + User user = new User(userId.getId(), + PetName, + PetWeight, + AccessToken, + ExpiresInTime, + RefreshToken, + RefreshTokenExpiresInTime); + + // Database에 Data 저장. + userRepository.insertUserInformation(user); + } +} diff --git a/docker-test-server/src/main/java/com/example/server/service/UpdateAccessService.java b/docker-test-server/src/main/java/com/example/server/service/UpdateAccessService.java new file mode 100644 index 0000000..df732c3 --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/service/UpdateAccessService.java @@ -0,0 +1,55 @@ +package com.example.server.service; + +import java.time.LocalDateTime; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import com.example.server.error.CException; +import com.example.server.error.ErrorCode; +import com.example.server.model.UserId; +import com.example.server.repository.UserRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class UpdateAccessService { + private String GET_USER_INFORMATION_URL="https://kapi.kakao.com/v2/user/me"; + private RestTemplate restTemplate; + private UserRepository userRepository; + + public void updateAccessTokenById(String AccessToken, Integer ExpiresIn) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + headers.add("Authorization", "Bearer " + AccessToken); + headers.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); + + HttpEntity> httpEntity = new HttpEntity<>(headers); + + ResponseEntity responseEntity; + try { + responseEntity = restTemplate.postForEntity( + GET_USER_INFORMATION_URL, + httpEntity, + UserId.class + ); + } catch(Exception e) { + throw new CException(ErrorCode.INVALID_TOKEN); + } + + UserId userId = responseEntity.getBody(); + if(userId == null) { + throw new CException(ErrorCode.INTERNAL_SERVER_ERROR); + } + + LocalDateTime ExpiresInTime = LocalDateTime.now().plusSeconds(ExpiresIn); + + userRepository.updateAccessTokenById(userId.getId(), AccessToken, ExpiresInTime); + } +} diff --git a/docker-test-server/src/main/java/com/example/server/service/UserService.java b/docker-test-server/src/main/java/com/example/server/service/UserService.java new file mode 100644 index 0000000..85d8688 --- /dev/null +++ b/docker-test-server/src/main/java/com/example/server/service/UserService.java @@ -0,0 +1,22 @@ +package com.example.server.service; + +import org.springframework.stereotype.Service; + +import com.example.server.model.User; +import com.example.server.repository.UserRepository; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class UserService { + private UserRepository userRepository; + + public User getPetInformation(String AccessToken) throws Exception{ + return userRepository.getUserInformationByAccessToken(AccessToken); + } + + public void setPetInformation(String AccessToken, String PetName, Integer PetWeight) throws Exception { + userRepository.setUserPetInformationByAccessToken(AccessToken, PetName, PetWeight); + } +} diff --git a/docker-test-server/src/main/java/com/example/server/user/UserController.java b/docker-test-server/src/main/java/com/example/server/user/UserController.java deleted file mode 100644 index 6055056..0000000 --- a/docker-test-server/src/main/java/com/example/server/user/UserController.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.example.server.user; - -import java.util.HashMap; -import java.util.List; - -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.bind.annotation.RequestMapping; - -import com.example.server.jwt.JwtProvider; -import com.example.server.kakao.KakaoApi; - -import lombok.RequiredArgsConstructor; - -// User will use -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/user") -public class UserController { - private final UserRepository userRepository; - private final JwtProvider jwtProvider = new JwtProvider(); - - @GetMapping("/database") - public ResponseEntity findAll() { - List userList = userRepository.findAll(); - return ResponseEntity.ok().body(userList); - } - - @GetMapping("/test") - public ResponseEntity test() { - return ResponseEntity.ok().body("test"); - } - - // 앱 첫 실행시. - // 1. jwt토큰을 kakako토큰으로 변환. - // 2-1. 유효하지 않은 토큰인 경우, 로그인 화면으로 이동. - // 2-2. 유효한 토큰인 경우, 재발급 후 홈화면 이동 (자동 로그인) - @GetMapping("/checktoken") - public ResponseEntity checkToken(@RequestHeader("accessToken") String accesstokenJWT, - @RequestHeader("refreshToken") String refreshtokenJWT) - throws Exception { - KakaoApi kakaoApi = new KakaoApi(); - HashMap map = kakaoApi.validateToken(accesstokenJWT, refreshtokenJWT); - - if (map.get("status").equals("false")) { - return ResponseEntity - .status(401) - .header("Location", "/api/user/login") - .build(); - } - - return ResponseEntity - .status(200) - .header("AccessToken", map.get("accessTokenJWT")) - .header("RefreshToken", map.get("refreshTokenJWT")) - .header("Location", "/api/home") - .build(); - } - - // petsetting 유저의 펫 이름과 몸무게 설정 후 헤더로 받음. 이후 데이터베이스에 업데이트 - // 헤더 : Email, 펫 이름, 펫 몸무게. - @PutMapping("/petsetting") - public ResponseEntity petSetting(@RequestHeader("email") String email, - @RequestHeader("petname") String petname, - @RequestHeader("petweight") Integer petweight) throws Exception { - - userRepository.updateUserByEmail(petname, petweight, email); - - return ResponseEntity - .status(200) - .header("Location", "/api/home") - .build(); - } - - @PatchMapping("/petsetting") - public ResponseEntity petSetting(@RequestHeader("accessToken") String accesstokenJWT, - @RequestHeader("refreshToken") String refreshtokenJWT, - @RequestHeader("petname") String petname, - @RequestHeader("petweight") Integer petweight) throws Exception { - - KakaoApi kakaoApi = new KakaoApi(); - HashMap map = kakaoApi.validateToken(accesstokenJWT, refreshtokenJWT); - - if (map.get("status").equals("false")) { - return ResponseEntity - .status(401) - .header("Location", "/api/user/login") - .build(); - } - - userRepository.updateUserByAccessToken(petname, petweight, jwtProvider.decodeJWTToken(accesstokenJWT)); - - return ResponseEntity - .status(200) - .header("Location", "") - .build(); - } -} \ No newline at end of file diff --git a/docker-test-server/src/main/java/com/example/server/user/UserRepository.java b/docker-test-server/src/main/java/com/example/server/user/UserRepository.java deleted file mode 100644 index d74a50c..0000000 --- a/docker-test-server/src/main/java/com/example/server/user/UserRepository.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.example.server.user; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.transaction.annotation.Transactional; - -public interface UserRepository extends JpaRepository { - User findByEmail(@Param("email") String email); - - User findByAccessToken(@Param("accessToken") String accessToken); - - User findByRefreshToken(@Param("refreshToken") String refreshToken); - - @Modifying - @Transactional - @Query("UPDATE User u SET u.petName = :petName, u.petWeight = :petWeight WHERE u.email = :email") - void updateUserByEmail(@Param("petName") String petName, @Param("petWeight") Integer petWeight, - @Param("email") String email); - - @Modifying - @Transactional - @Query("UPDATE User u SET u.petName = :petName, u.petWeight = :petWeight WHERE u.accessToken = :accessToken") - void updateUserByAccessToken(@Param("petName") String petName, @Param("petWeight") Integer petWeight, - @Param("accessToken") String accessToken); - - @Modifying - @Transactional - @Query("UPDATE User u SET u.accessToken = :accessToken WHERE u.refreshToken = :refreshToken") - void updateUserByRefreshToken(@Param("accessToken") String accessToken, @Param("refreshToken") String refreshToken); -} \ No newline at end of file diff --git a/docker-test-server/src/main/java/com/example/server/user/UserService.java b/docker-test-server/src/main/java/com/example/server/user/UserService.java deleted file mode 100644 index 6cde9de..0000000 --- a/docker-test-server/src/main/java/com/example/server/user/UserService.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.example.server.user; - -import org.springframework.stereotype.Service; - -import lombok.RequiredArgsConstructor; - -@Service -@RequiredArgsConstructor -public class UserService { - -}