From baae3d6e1bc1901d15a1eec069c31fa35d59a2cc Mon Sep 17 00:00:00 2001 From: jsy3831 Date: Fri, 10 Sep 2021 15:14:57 +0900 Subject: [PATCH 1/8] =?UTF-8?q?#2=20=EC=A4=91=EB=B3=B5=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80=20=20/=20?= =?UTF-8?q?=EB=82=B4=EC=A0=95=EB=B3=B4=EC=A1=B0=ED=9A=8C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- .../com/photobook/controller/UserController.java | 13 +++++++++++-- .../photobook/exception/DuplicateException.java | 8 ++++++++ .../com/photobook/exception/ExceptionAdvice.java | 14 ++++++++++---- 4 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/photobook/exception/DuplicateException.java diff --git a/README.md b/README.md index fb7f386..c61432f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# simple_sns +# photobook instagram과 유사한 sns 서비스 입니다. diff --git a/src/main/java/com/photobook/controller/UserController.java b/src/main/java/com/photobook/controller/UserController.java index 885a46e..dac5c03 100644 --- a/src/main/java/com/photobook/controller/UserController.java +++ b/src/main/java/com/photobook/controller/UserController.java @@ -2,6 +2,7 @@ import com.photobook.annotation.LoginCheck; import com.photobook.dto.UserDto; +import com.photobook.exception.DuplicateException; import com.photobook.service.LoginService; import com.photobook.service.UserService; import org.springframework.validation.annotation.Validated; @@ -25,6 +26,11 @@ public UserController(UserService userService, LoginService loginService) { @PostMapping("/login") public void login(@RequestParam @NotBlank String id, @RequestParam @NotBlank String password) { + + if(loginService.getLoginUserInfo() != null) { + throw new DuplicateException("이미 로그인된 상태입니다."); + } + UserDto userInfo = userService.getUserInfoByIdAndPassword(id, password); loginService.setLoginUserInfo(userInfo); @@ -36,10 +42,13 @@ public void logout() { loginService.removeLoginUserInfo(); } - @GetMapping("/{id}") + @GetMapping("/myInfo") @LoginCheck - public UserDto getUserInfoById(@PathVariable @NotBlank String id) { + public UserDto getUserInfoById() { + String id = loginService.getLoginUserInfo().getId(); + UserDto userInfo = userService.getUserInfoById(id); + return userInfo; } } \ No newline at end of file diff --git a/src/main/java/com/photobook/exception/DuplicateException.java b/src/main/java/com/photobook/exception/DuplicateException.java new file mode 100644 index 0000000..73ef587 --- /dev/null +++ b/src/main/java/com/photobook/exception/DuplicateException.java @@ -0,0 +1,8 @@ +package com.photobook.exception; + +public class DuplicateException extends RuntimeException{ + + public DuplicateException(String message) { + super(message); + } +} diff --git a/src/main/java/com/photobook/exception/ExceptionAdvice.java b/src/main/java/com/photobook/exception/ExceptionAdvice.java index 4901e45..1389cef 100644 --- a/src/main/java/com/photobook/exception/ExceptionAdvice.java +++ b/src/main/java/com/photobook/exception/ExceptionAdvice.java @@ -11,25 +11,31 @@ public class ExceptionAdvice { @ExceptionHandler(Exception.class) - public String exception(Exception e) { + public String handleException(Exception e) { return e.getMessage(); } @ExceptionHandler(IllegalArgumentException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) - public String illegalArgumentException(IllegalArgumentException e) { + public String handleIllegalArgumentException(IllegalArgumentException e) { return e.getMessage(); } @ExceptionHandler(ConstraintViolationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) - public String constraintViolationException(ConstraintViolationException e) { + public String handleConstraintViolationException(ConstraintViolationException e) { return e.getMessage(); } @ExceptionHandler(UnauthorizedException.class) @ResponseStatus(HttpStatus.UNAUTHORIZED) - public String unauthorizedException(UnauthorizedException e) { + public String handleUnauthorizedException(UnauthorizedException e) { + return e.getMessage(); + } + + @ExceptionHandler(DuplicateException.class) + @ResponseStatus(HttpStatus.CONFLICT) + public String handleDuplicateException(DuplicateException e) { return e.getMessage(); } From b56bb86b5e19882a4f34eeef70128ff930762449 Mon Sep 17 00:00:00 2001 From: jsy3831 Date: Fri, 10 Sep 2021 16:38:18 +0900 Subject: [PATCH 2/8] =?UTF-8?q?aspect=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annotation/LoginCheckAndReturnUserInfo.java | 9 +++++++++ .../java/com/photobook/aop/AuthCheckAspect.java | 16 +++++++++++++++- .../com/photobook/controller/UserController.java | 9 ++++++--- 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/photobook/annotation/LoginCheckAndReturnUserInfo.java diff --git a/src/main/java/com/photobook/annotation/LoginCheckAndReturnUserInfo.java b/src/main/java/com/photobook/annotation/LoginCheckAndReturnUserInfo.java new file mode 100644 index 0000000..e3b0855 --- /dev/null +++ b/src/main/java/com/photobook/annotation/LoginCheckAndReturnUserInfo.java @@ -0,0 +1,9 @@ +package com.photobook.annotation; + +import java.lang.annotation.*; + +@Documented +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.CLASS) +public @interface LoginCheckAndReturnUserInfo { +} diff --git a/src/main/java/com/photobook/aop/AuthCheckAspect.java b/src/main/java/com/photobook/aop/AuthCheckAspect.java index a9d1c67..718bb0f 100644 --- a/src/main/java/com/photobook/aop/AuthCheckAspect.java +++ b/src/main/java/com/photobook/aop/AuthCheckAspect.java @@ -3,6 +3,8 @@ import com.photobook.exception.UnauthorizedException; import com.photobook.dto.UserDto; import com.photobook.service.LoginService; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @@ -18,13 +20,25 @@ public AuthCheckAspect(LoginService loginService) { } @Before("@annotation(com.photobook.annotation.LoginCheck)") - public void loginCheck() { + public UserDto loginCheck() { UserDto userInfo = loginService.getLoginUserInfo(); if(userInfo == null) { throw new UnauthorizedException("로그인된 사용자 정보가 존재하지 않습니다."); } + + return userInfo; + } + + @Around("@annotation(com.photobook.annotation.LoginCheckAndReturnUserInfo)") + public Object loginCheckAndReturnUserInfo(ProceedingJoinPoint pjp) throws Throwable { + + UserDto userInfo = loginCheck(); + + Object result = pjp.proceed(new Object[] { userInfo }); + + return result; } } diff --git a/src/main/java/com/photobook/controller/UserController.java b/src/main/java/com/photobook/controller/UserController.java index dac5c03..7c675c3 100644 --- a/src/main/java/com/photobook/controller/UserController.java +++ b/src/main/java/com/photobook/controller/UserController.java @@ -1,6 +1,7 @@ package com.photobook.controller; import com.photobook.annotation.LoginCheck; +import com.photobook.annotation.LoginCheckAndReturnUserInfo; import com.photobook.dto.UserDto; import com.photobook.exception.DuplicateException; import com.photobook.service.LoginService; @@ -43,12 +44,14 @@ public void logout() { } @GetMapping("/myInfo") - @LoginCheck - public UserDto getUserInfoById() { - String id = loginService.getLoginUserInfo().getId(); + @LoginCheckAndReturnUserInfo + public UserDto getUserInfoById(@ModelAttribute UserDto userDto) { + + String id = userDto.getId(); UserDto userInfo = userService.getUserInfoById(id); return userInfo; } + } \ No newline at end of file From 1959f0aaf11302a8b8ae855e154f281e1f0c6eb8 Mon Sep 17 00:00:00 2001 From: jsy3831 Date: Sat, 11 Sep 2021 22:00:52 +0900 Subject: [PATCH 3/8] =?UTF-8?q?error=ED=98=95=EC=8B=9D=20enum=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=EC=A0=95=EC=9D=98=20/=20error=20response=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=EC=B6=94=EA=B0=80=20/=20controllerAdivce?= =?UTF-8?q?=20response=20=EA=B7=9C=EA=B2=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/photobook/aop/AuthCheckAspect.java | 5 ++- .../photobook/controller/UserController.java | 14 +++--- src/main/java/com/photobook/dto/UserDto.java | 1 - .../photobook/exception/CustomException.java | 10 +++++ .../exception/DuplicateException.java | 8 ---- .../com/photobook/exception/ErrorCode.java | 21 +++++++++ .../photobook/exception/ErrorResponse.java | 45 +++++++++++++++++++ .../photobook/exception/ExceptionAdvice.java | 31 +++++-------- .../exception/UnauthorizedException.java | 8 ---- 9 files changed, 98 insertions(+), 45 deletions(-) create mode 100644 src/main/java/com/photobook/exception/CustomException.java delete mode 100644 src/main/java/com/photobook/exception/DuplicateException.java create mode 100644 src/main/java/com/photobook/exception/ErrorCode.java create mode 100644 src/main/java/com/photobook/exception/ErrorResponse.java delete mode 100644 src/main/java/com/photobook/exception/UnauthorizedException.java diff --git a/src/main/java/com/photobook/aop/AuthCheckAspect.java b/src/main/java/com/photobook/aop/AuthCheckAspect.java index 718bb0f..b1af220 100644 --- a/src/main/java/com/photobook/aop/AuthCheckAspect.java +++ b/src/main/java/com/photobook/aop/AuthCheckAspect.java @@ -1,6 +1,7 @@ package com.photobook.aop; -import com.photobook.exception.UnauthorizedException; +import com.photobook.exception.CustomException; +import com.photobook.exception.ErrorCode; import com.photobook.dto.UserDto; import com.photobook.service.LoginService; import org.aspectj.lang.ProceedingJoinPoint; @@ -25,7 +26,7 @@ public UserDto loginCheck() { UserDto userInfo = loginService.getLoginUserInfo(); if(userInfo == null) { - throw new UnauthorizedException("로그인된 사용자 정보가 존재하지 않습니다."); + throw new CustomException(ErrorCode.NOT_LOGIN); } return userInfo; diff --git a/src/main/java/com/photobook/controller/UserController.java b/src/main/java/com/photobook/controller/UserController.java index 7c675c3..1381de8 100644 --- a/src/main/java/com/photobook/controller/UserController.java +++ b/src/main/java/com/photobook/controller/UserController.java @@ -3,13 +3,15 @@ import com.photobook.annotation.LoginCheck; import com.photobook.annotation.LoginCheckAndReturnUserInfo; import com.photobook.dto.UserDto; -import com.photobook.exception.DuplicateException; +import com.photobook.exception.CustomException; +import com.photobook.exception.ErrorCode; import com.photobook.service.LoginService; import com.photobook.service.UserService; +import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; - import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; @RestController @RequestMapping("/users") @@ -29,7 +31,7 @@ public UserController(UserService userService, LoginService loginService) { public void login(@RequestParam @NotBlank String id, @RequestParam @NotBlank String password) { if(loginService.getLoginUserInfo() != null) { - throw new DuplicateException("이미 로그인된 상태입니다."); + throw new CustomException(ErrorCode.DUPLICATE_LOGIN); } UserDto userInfo = userService.getUserInfoByIdAndPassword(id, password); @@ -45,13 +47,13 @@ public void logout() { @GetMapping("/myInfo") @LoginCheckAndReturnUserInfo - public UserDto getUserInfoById(@ModelAttribute UserDto userDto) { + public ResponseEntity getUserInfoById(@ModelAttribute @NotNull UserDto userDto) { - String id = userDto.getId(); + String id = userDto.getId(); // Aspect에서 받아온 값 UserDto userInfo = userService.getUserInfoById(id); - return userInfo; + return ResponseEntity.ok(userInfo); } } \ No newline at end of file diff --git a/src/main/java/com/photobook/dto/UserDto.java b/src/main/java/com/photobook/dto/UserDto.java index f35b837..8fb03e2 100644 --- a/src/main/java/com/photobook/dto/UserDto.java +++ b/src/main/java/com/photobook/dto/UserDto.java @@ -2,7 +2,6 @@ import lombok.Getter; import lombok.Setter; - import java.time.LocalDate; @Getter diff --git a/src/main/java/com/photobook/exception/CustomException.java b/src/main/java/com/photobook/exception/CustomException.java new file mode 100644 index 0000000..c87c562 --- /dev/null +++ b/src/main/java/com/photobook/exception/CustomException.java @@ -0,0 +1,10 @@ +package com.photobook.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class CustomException extends RuntimeException { + private final ErrorCode errorCode; +} diff --git a/src/main/java/com/photobook/exception/DuplicateException.java b/src/main/java/com/photobook/exception/DuplicateException.java deleted file mode 100644 index 73ef587..0000000 --- a/src/main/java/com/photobook/exception/DuplicateException.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.photobook.exception; - -public class DuplicateException extends RuntimeException{ - - public DuplicateException(String message) { - super(message); - } -} diff --git a/src/main/java/com/photobook/exception/ErrorCode.java b/src/main/java/com/photobook/exception/ErrorCode.java new file mode 100644 index 0000000..5c0e61a --- /dev/null +++ b/src/main/java/com/photobook/exception/ErrorCode.java @@ -0,0 +1,21 @@ +package com.photobook.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ErrorCode { + + /* 401 UNAUTHORIZED */ + NOT_LOGIN(HttpStatus.UNAUTHORIZED, "로그인된 사용자 정보가 존재하지 않습니다."), + + /* 409 CONFLICT */ + DUPLICATE_LOGIN(HttpStatus.CONFLICT, "이미 로그인된 상태입니다.") + + ; + + private final HttpStatus httpStatus; + private final String message; +} diff --git a/src/main/java/com/photobook/exception/ErrorResponse.java b/src/main/java/com/photobook/exception/ErrorResponse.java new file mode 100644 index 0000000..51945c8 --- /dev/null +++ b/src/main/java/com/photobook/exception/ErrorResponse.java @@ -0,0 +1,45 @@ +package com.photobook.exception; + +import lombok.*; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import java.time.LocalDateTime; + +@Getter +@Builder +public class ErrorResponse { + + private final LocalDateTime timestamp = LocalDateTime.now(); + + private final int status; + + private final String error; + + private final String code; + + private final String message; + + public static ResponseEntity toResponseEntity(ErrorCode errorCode) { + return ResponseEntity + .status(errorCode.getHttpStatus()) + .body(ErrorResponse.builder() + .status(errorCode.getHttpStatus().value()) + .error(errorCode.getHttpStatus().name()) + .code(errorCode.name()) + .message(errorCode.getMessage()) + .build() + ); + } + + public static ResponseEntity toResponseEntity(HttpStatus httpStatus, Exception e) { + return ResponseEntity + .status(httpStatus) + .body(ErrorResponse.builder() + .status(httpStatus.value()) + .error(httpStatus.name()) + .message(e.getMessage()) + .build() + ); + } + +} diff --git a/src/main/java/com/photobook/exception/ExceptionAdvice.java b/src/main/java/com/photobook/exception/ExceptionAdvice.java index 1389cef..e92b2b4 100644 --- a/src/main/java/com/photobook/exception/ExceptionAdvice.java +++ b/src/main/java/com/photobook/exception/ExceptionAdvice.java @@ -1,8 +1,8 @@ package com.photobook.exception; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.validation.ConstraintViolationException; @@ -11,32 +11,23 @@ public class ExceptionAdvice { @ExceptionHandler(Exception.class) - public String handleException(Exception e) { - return e.getMessage(); - } - - @ExceptionHandler(IllegalArgumentException.class) - @ResponseStatus(HttpStatus.BAD_REQUEST) - public String handleIllegalArgumentException(IllegalArgumentException e) { - return e.getMessage(); + public ResponseEntity handleException(Exception e) { + return ErrorResponse.toResponseEntity(HttpStatus.BAD_REQUEST, e); } @ExceptionHandler(ConstraintViolationException.class) - @ResponseStatus(HttpStatus.BAD_REQUEST) - public String handleConstraintViolationException(ConstraintViolationException e) { - return e.getMessage(); + public ResponseEntity handleConstraintViolationException(ConstraintViolationException e) { + return ErrorResponse.toResponseEntity(HttpStatus.BAD_REQUEST, e); } - @ExceptionHandler(UnauthorizedException.class) - @ResponseStatus(HttpStatus.UNAUTHORIZED) - public String handleUnauthorizedException(UnauthorizedException e) { - return e.getMessage(); + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity handleIllegalArgumentException(IllegalArgumentException e) { + return ErrorResponse.toResponseEntity(HttpStatus.BAD_REQUEST, e); } - @ExceptionHandler(DuplicateException.class) - @ResponseStatus(HttpStatus.CONFLICT) - public String handleDuplicateException(DuplicateException e) { - return e.getMessage(); + @ExceptionHandler(CustomException.class) + public ResponseEntity handleCustomException(CustomException e) { + return ErrorResponse.toResponseEntity(e.getErrorCode()); } } diff --git a/src/main/java/com/photobook/exception/UnauthorizedException.java b/src/main/java/com/photobook/exception/UnauthorizedException.java deleted file mode 100644 index 5ffc24a..0000000 --- a/src/main/java/com/photobook/exception/UnauthorizedException.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.photobook.exception; - -public class UnauthorizedException extends RuntimeException { - - public UnauthorizedException(String message) { - super(message); - } -} From e4baebd0136649979ae07f0a1afd3d7443e74946 Mon Sep 17 00:00:00 2001 From: jsy3831 Date: Sat, 11 Sep 2021 22:43:36 +0900 Subject: [PATCH 4/8] =?UTF-8?q?slf4j=20=EC=9E=84=EC=8B=9C=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/photobook/exception/ExceptionAdvice.java | 6 ++++++ src/main/resources/application.properties | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/photobook/exception/ExceptionAdvice.java b/src/main/java/com/photobook/exception/ExceptionAdvice.java index e92b2b4..844f00d 100644 --- a/src/main/java/com/photobook/exception/ExceptionAdvice.java +++ b/src/main/java/com/photobook/exception/ExceptionAdvice.java @@ -1,5 +1,6 @@ package com.photobook.exception; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -7,26 +8,31 @@ import javax.validation.ConstraintViolationException; +@Slf4j @RestControllerAdvice public class ExceptionAdvice { @ExceptionHandler(Exception.class) public ResponseEntity handleException(Exception e) { + log.error("handleException throw Exception : {}", e.getMessage()); return ErrorResponse.toResponseEntity(HttpStatus.BAD_REQUEST, e); } @ExceptionHandler(ConstraintViolationException.class) public ResponseEntity handleConstraintViolationException(ConstraintViolationException e) { + log.error("handleConstraintViolationException throw ConstraintViolationException : {}", e.getMessage()); return ErrorResponse.toResponseEntity(HttpStatus.BAD_REQUEST, e); } @ExceptionHandler(IllegalArgumentException.class) public ResponseEntity handleIllegalArgumentException(IllegalArgumentException e) { + log.error("handleIllegalArgumentException throw IllegalArgumentException : {}", e.getMessage()); return ErrorResponse.toResponseEntity(HttpStatus.BAD_REQUEST, e); } @ExceptionHandler(CustomException.class) public ResponseEntity handleCustomException(CustomException e) { + log.error("handleCustomException throw CustomException : {}", e.getErrorCode()); return ErrorResponse.toResponseEntity(e.getErrorCode()); } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index ed3e529..d0b885d 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -6,4 +6,3 @@ spring.datasource.driver-class-name=net.sf.log4jdbc.sql.jdbcapi.DriverSpy #spring.session.store-type=redis #spring.redis.host=localhost #spring.redis.port=6379 - From 29b59c67e04a8b03d4f8c2556c396315c721305d Mon Sep 17 00:00:00 2001 From: jsy3831 Date: Thu, 16 Sep 2021 16:39:06 +0900 Subject: [PATCH 5/8] =?UTF-8?q?#2=20argumentResolver=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20/=20response=20=EB=A6=AC=EB=B7=B0=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoginCheckAndReturnUserInfo.java | 9 --- .../com/photobook/annotation/LoginUser.java | 9 +++ .../com/photobook/aop/AuthCheckAspect.java | 20 +------ .../com/photobook/config/WebMvcConfig.java | 24 ++++++++ .../photobook/controller/UserController.java | 29 +++++----- .../LoginUserArgumentResolver.java | 31 +++++++++++ .../controller/response/Response.java | 37 +++++++++++++ src/main/java/com/photobook/dto/UserDto.java | 6 +- .../photobook/exception/CustomException.java | 10 ---- .../com/photobook/exception/ErrorCode.java | 21 ------- .../photobook/exception/ErrorResponse.java | 45 --------------- .../photobook/exception/ExceptionAdvice.java | 39 ------------- .../exception/ExceptionController.java | 55 +++++++++++++++++++ .../photobook/exception/ExceptionType.java | 14 +++++ .../customException/DuplicatedException.java | 8 +++ .../UnauthorizedException.java | 8 +++ src/main/resources/log4jdbc.log4j2.properties | 6 +- 17 files changed, 214 insertions(+), 157 deletions(-) delete mode 100644 src/main/java/com/photobook/annotation/LoginCheckAndReturnUserInfo.java create mode 100644 src/main/java/com/photobook/annotation/LoginUser.java create mode 100644 src/main/java/com/photobook/config/WebMvcConfig.java create mode 100644 src/main/java/com/photobook/controller/argumentResolver/LoginUserArgumentResolver.java create mode 100644 src/main/java/com/photobook/controller/response/Response.java delete mode 100644 src/main/java/com/photobook/exception/CustomException.java delete mode 100644 src/main/java/com/photobook/exception/ErrorCode.java delete mode 100644 src/main/java/com/photobook/exception/ErrorResponse.java delete mode 100644 src/main/java/com/photobook/exception/ExceptionAdvice.java create mode 100644 src/main/java/com/photobook/exception/ExceptionController.java create mode 100644 src/main/java/com/photobook/exception/ExceptionType.java create mode 100644 src/main/java/com/photobook/exception/customException/DuplicatedException.java create mode 100644 src/main/java/com/photobook/exception/customException/UnauthorizedException.java diff --git a/src/main/java/com/photobook/annotation/LoginCheckAndReturnUserInfo.java b/src/main/java/com/photobook/annotation/LoginCheckAndReturnUserInfo.java deleted file mode 100644 index e3b0855..0000000 --- a/src/main/java/com/photobook/annotation/LoginCheckAndReturnUserInfo.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.photobook.annotation; - -import java.lang.annotation.*; - -@Documented -@Target(ElementType.METHOD) -@Retention(RetentionPolicy.CLASS) -public @interface LoginCheckAndReturnUserInfo { -} diff --git a/src/main/java/com/photobook/annotation/LoginUser.java b/src/main/java/com/photobook/annotation/LoginUser.java new file mode 100644 index 0000000..d2673b9 --- /dev/null +++ b/src/main/java/com/photobook/annotation/LoginUser.java @@ -0,0 +1,9 @@ +package com.photobook.annotation; + +import java.lang.annotation.*; + +@Documented +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface LoginUser { +} diff --git a/src/main/java/com/photobook/aop/AuthCheckAspect.java b/src/main/java/com/photobook/aop/AuthCheckAspect.java index b1af220..006f442 100644 --- a/src/main/java/com/photobook/aop/AuthCheckAspect.java +++ b/src/main/java/com/photobook/aop/AuthCheckAspect.java @@ -1,11 +1,8 @@ package com.photobook.aop; -import com.photobook.exception.CustomException; -import com.photobook.exception.ErrorCode; import com.photobook.dto.UserDto; +import com.photobook.exception.customException.UnauthorizedException; import com.photobook.service.LoginService; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @@ -21,25 +18,14 @@ public AuthCheckAspect(LoginService loginService) { } @Before("@annotation(com.photobook.annotation.LoginCheck)") - public UserDto loginCheck() { + public void loginCheck() { UserDto userInfo = loginService.getLoginUserInfo(); if(userInfo == null) { - throw new CustomException(ErrorCode.NOT_LOGIN); + throw new UnauthorizedException("로그인된 사용자 정보가 존재하지 않습니다."); } - return userInfo; - } - - @Around("@annotation(com.photobook.annotation.LoginCheckAndReturnUserInfo)") - public Object loginCheckAndReturnUserInfo(ProceedingJoinPoint pjp) throws Throwable { - - UserDto userInfo = loginCheck(); - - Object result = pjp.proceed(new Object[] { userInfo }); - - return result; } } diff --git a/src/main/java/com/photobook/config/WebMvcConfig.java b/src/main/java/com/photobook/config/WebMvcConfig.java new file mode 100644 index 0000000..3bff21a --- /dev/null +++ b/src/main/java/com/photobook/config/WebMvcConfig.java @@ -0,0 +1,24 @@ +package com.photobook.config; + +import com.photobook.controller.argumentResolver.LoginUserArgumentResolver; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + private final LoginUserArgumentResolver loginUserArgumentResolver; + + public WebMvcConfig(LoginUserArgumentResolver loginUserArgumentResolver) { + this.loginUserArgumentResolver = loginUserArgumentResolver; + } + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(loginUserArgumentResolver); + } + +} diff --git a/src/main/java/com/photobook/controller/UserController.java b/src/main/java/com/photobook/controller/UserController.java index 1381de8..b2a9909 100644 --- a/src/main/java/com/photobook/controller/UserController.java +++ b/src/main/java/com/photobook/controller/UserController.java @@ -1,17 +1,16 @@ package com.photobook.controller; import com.photobook.annotation.LoginCheck; -import com.photobook.annotation.LoginCheckAndReturnUserInfo; +import com.photobook.annotation.LoginUser; +import com.photobook.controller.response.Response; import com.photobook.dto.UserDto; -import com.photobook.exception.CustomException; -import com.photobook.exception.ErrorCode; +import com.photobook.exception.customException.DuplicatedException; import com.photobook.service.LoginService; import com.photobook.service.UserService; -import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; + import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; @RestController @RequestMapping("/users") @@ -28,32 +27,34 @@ public UserController(UserService userService, LoginService loginService) { } @PostMapping("/login") - public void login(@RequestParam @NotBlank String id, @RequestParam @NotBlank String password) { + public Response login(@RequestParam @NotBlank String id, @RequestParam @NotBlank String password) { if(loginService.getLoginUserInfo() != null) { - throw new CustomException(ErrorCode.DUPLICATE_LOGIN); + throw new DuplicatedException("이미 로그인된 상태입니다."); } UserDto userInfo = userService.getUserInfoByIdAndPassword(id, password); loginService.setLoginUserInfo(userInfo); + + return Response.toResponse(200, "로그인 하였습니다.", userInfo); } @PostMapping("/logout") @LoginCheck - public void logout() { + public Response logout() { loginService.removeLoginUserInfo(); - } - @GetMapping("/myInfo") - @LoginCheckAndReturnUserInfo - public ResponseEntity getUserInfoById(@ModelAttribute @NotNull UserDto userDto) { + return Response.toResponse(200, "로그아웃 하였습니다."); + } - String id = userDto.getId(); // Aspect에서 받아온 값 + @GetMapping("/my-info") + @LoginCheck + public Response getUserInfoById(@LoginUser String id) { UserDto userInfo = userService.getUserInfoById(id); - return ResponseEntity.ok(userInfo); + return Response.toResponse(200, "회원정보 조회", userInfo); } } \ No newline at end of file diff --git a/src/main/java/com/photobook/controller/argumentResolver/LoginUserArgumentResolver.java b/src/main/java/com/photobook/controller/argumentResolver/LoginUserArgumentResolver.java new file mode 100644 index 0000000..feb8671 --- /dev/null +++ b/src/main/java/com/photobook/controller/argumentResolver/LoginUserArgumentResolver.java @@ -0,0 +1,31 @@ +package com.photobook.controller.argumentResolver; + +import com.photobook.annotation.LoginUser; +import com.photobook.service.LoginService; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +@Component +public class LoginUserArgumentResolver implements HandlerMethodArgumentResolver { + + private final LoginService loginService; + + public LoginUserArgumentResolver(LoginService loginService) { + this.loginService = loginService; + } + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(LoginUser.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { + return loginService.getLoginUserInfo().getId(); + } +} diff --git a/src/main/java/com/photobook/controller/response/Response.java b/src/main/java/com/photobook/controller/response/Response.java new file mode 100644 index 0000000..8c9a5c0 --- /dev/null +++ b/src/main/java/com/photobook/controller/response/Response.java @@ -0,0 +1,37 @@ +package com.photobook.controller.response; + +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +public class Response { + + private final LocalDateTime timestamp = LocalDateTime.now(); + + private final int code; + + private final String message; + + private final T body; + + @Builder + Response(final int code, final String message, final T body) { + this.code = code; + this.message = message; + this.body = body; + } + + public static Response toResponse(int code, String message) { + return toResponse(code, message, null); + } + + public static Response toResponse(int code, String message, T body) { + return Response.builder() + .code(code) + .message(message) + .body(body) + .build(); + } +} diff --git a/src/main/java/com/photobook/dto/UserDto.java b/src/main/java/com/photobook/dto/UserDto.java index 8fb03e2..715e9b9 100644 --- a/src/main/java/com/photobook/dto/UserDto.java +++ b/src/main/java/com/photobook/dto/UserDto.java @@ -2,11 +2,15 @@ import lombok.Getter; import lombok.Setter; +import lombok.ToString; + +import java.io.Serializable; import java.time.LocalDate; @Getter @Setter -public class UserDto { +@ToString +public class UserDto implements Serializable { private int userId; diff --git a/src/main/java/com/photobook/exception/CustomException.java b/src/main/java/com/photobook/exception/CustomException.java deleted file mode 100644 index c87c562..0000000 --- a/src/main/java/com/photobook/exception/CustomException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.photobook.exception; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Getter -@AllArgsConstructor -public class CustomException extends RuntimeException { - private final ErrorCode errorCode; -} diff --git a/src/main/java/com/photobook/exception/ErrorCode.java b/src/main/java/com/photobook/exception/ErrorCode.java deleted file mode 100644 index 5c0e61a..0000000 --- a/src/main/java/com/photobook/exception/ErrorCode.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.photobook.exception; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -@AllArgsConstructor -public enum ErrorCode { - - /* 401 UNAUTHORIZED */ - NOT_LOGIN(HttpStatus.UNAUTHORIZED, "로그인된 사용자 정보가 존재하지 않습니다."), - - /* 409 CONFLICT */ - DUPLICATE_LOGIN(HttpStatus.CONFLICT, "이미 로그인된 상태입니다.") - - ; - - private final HttpStatus httpStatus; - private final String message; -} diff --git a/src/main/java/com/photobook/exception/ErrorResponse.java b/src/main/java/com/photobook/exception/ErrorResponse.java deleted file mode 100644 index 51945c8..0000000 --- a/src/main/java/com/photobook/exception/ErrorResponse.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.photobook.exception; - -import lombok.*; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import java.time.LocalDateTime; - -@Getter -@Builder -public class ErrorResponse { - - private final LocalDateTime timestamp = LocalDateTime.now(); - - private final int status; - - private final String error; - - private final String code; - - private final String message; - - public static ResponseEntity toResponseEntity(ErrorCode errorCode) { - return ResponseEntity - .status(errorCode.getHttpStatus()) - .body(ErrorResponse.builder() - .status(errorCode.getHttpStatus().value()) - .error(errorCode.getHttpStatus().name()) - .code(errorCode.name()) - .message(errorCode.getMessage()) - .build() - ); - } - - public static ResponseEntity toResponseEntity(HttpStatus httpStatus, Exception e) { - return ResponseEntity - .status(httpStatus) - .body(ErrorResponse.builder() - .status(httpStatus.value()) - .error(httpStatus.name()) - .message(e.getMessage()) - .build() - ); - } - -} diff --git a/src/main/java/com/photobook/exception/ExceptionAdvice.java b/src/main/java/com/photobook/exception/ExceptionAdvice.java deleted file mode 100644 index 844f00d..0000000 --- a/src/main/java/com/photobook/exception/ExceptionAdvice.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.photobook.exception; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -import javax.validation.ConstraintViolationException; - -@Slf4j -@RestControllerAdvice -public class ExceptionAdvice { - - @ExceptionHandler(Exception.class) - public ResponseEntity handleException(Exception e) { - log.error("handleException throw Exception : {}", e.getMessage()); - return ErrorResponse.toResponseEntity(HttpStatus.BAD_REQUEST, e); - } - - @ExceptionHandler(ConstraintViolationException.class) - public ResponseEntity handleConstraintViolationException(ConstraintViolationException e) { - log.error("handleConstraintViolationException throw ConstraintViolationException : {}", e.getMessage()); - return ErrorResponse.toResponseEntity(HttpStatus.BAD_REQUEST, e); - } - - @ExceptionHandler(IllegalArgumentException.class) - public ResponseEntity handleIllegalArgumentException(IllegalArgumentException e) { - log.error("handleIllegalArgumentException throw IllegalArgumentException : {}", e.getMessage()); - return ErrorResponse.toResponseEntity(HttpStatus.BAD_REQUEST, e); - } - - @ExceptionHandler(CustomException.class) - public ResponseEntity handleCustomException(CustomException e) { - log.error("handleCustomException throw CustomException : {}", e.getErrorCode()); - return ErrorResponse.toResponseEntity(e.getErrorCode()); - } - -} diff --git a/src/main/java/com/photobook/exception/ExceptionController.java b/src/main/java/com/photobook/exception/ExceptionController.java new file mode 100644 index 0000000..5456fd6 --- /dev/null +++ b/src/main/java/com/photobook/exception/ExceptionController.java @@ -0,0 +1,55 @@ +package com.photobook.exception; + +import com.photobook.controller.response.Response; +import com.photobook.exception.customException.DuplicatedException; +import com.photobook.exception.customException.UnauthorizedException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import javax.validation.ConstraintViolationException; + +@Slf4j +@RestControllerAdvice +public class ExceptionController { + + private static ExceptionType exceptionType(Exception e) { + return new ExceptionType(e.getClass().getSimpleName()); + } + + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Response handleException(Exception e) { + log.error("handleException throw Exception : {}", e.getMessage()); + return Response.toResponse(400, e.getMessage(), exceptionType(e)); + } + + @ExceptionHandler(ConstraintViolationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Response handleConstraintViolationException(ConstraintViolationException e) { + log.error("handleConstraintViolationException throw ConstraintViolationException : {}", e.getMessage()); + return Response.toResponse(400, e.getMessage(), exceptionType(e)); + } + + @ExceptionHandler(IllegalArgumentException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Response handleIllegalArgumentException(IllegalArgumentException e) { + log.error("handleIllegalArgumentException throw IllegalArgumentException : {}", e.getMessage()); + return Response.toResponse(400, e.getMessage(), exceptionType(e)); + } + + @ExceptionHandler(UnauthorizedException.class) + @ResponseStatus(HttpStatus.UNAUTHORIZED) + public Response handleUnauthorizedException(UnauthorizedException e) { + return Response.toResponse(401, e.getMessage(), exceptionType(e)); + } + + @ExceptionHandler(DuplicatedException.class) + @ResponseStatus(HttpStatus.CONFLICT) + public Response handleDuplicatedException(DuplicatedException e) { + return Response.toResponse(409, e.getMessage(), exceptionType(e)); + } + +} diff --git a/src/main/java/com/photobook/exception/ExceptionType.java b/src/main/java/com/photobook/exception/ExceptionType.java new file mode 100644 index 0000000..4d8e3d3 --- /dev/null +++ b/src/main/java/com/photobook/exception/ExceptionType.java @@ -0,0 +1,14 @@ +package com.photobook.exception; + +import lombok.Getter; + +@Getter +public class ExceptionType { + + private final String type; + + ExceptionType(final String type) { + this.type = type; + } + +} diff --git a/src/main/java/com/photobook/exception/customException/DuplicatedException.java b/src/main/java/com/photobook/exception/customException/DuplicatedException.java new file mode 100644 index 0000000..2d07007 --- /dev/null +++ b/src/main/java/com/photobook/exception/customException/DuplicatedException.java @@ -0,0 +1,8 @@ +package com.photobook.exception.customException; + +public class DuplicatedException extends RuntimeException { + + public DuplicatedException(String message) { + super(message); + } +} diff --git a/src/main/java/com/photobook/exception/customException/UnauthorizedException.java b/src/main/java/com/photobook/exception/customException/UnauthorizedException.java new file mode 100644 index 0000000..30f33b5 --- /dev/null +++ b/src/main/java/com/photobook/exception/customException/UnauthorizedException.java @@ -0,0 +1,8 @@ +package com.photobook.exception.customException; + +public class UnauthorizedException extends RuntimeException { + + public UnauthorizedException(String message) { + super(message); + } +} diff --git a/src/main/resources/log4jdbc.log4j2.properties b/src/main/resources/log4jdbc.log4j2.properties index a48b3e9..7351bd1 100644 --- a/src/main/resources/log4jdbc.log4j2.properties +++ b/src/main/resources/log4jdbc.log4j2.properties @@ -1,2 +1,6 @@ log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator -log4jdbc.dump.sql.maxlinelength=0 \ No newline at end of file +log4jdbc.dump.sql.maxlinelength=0 + +# log4jdbc? ???? ??? ?? +log4jdbc.drivers=com.mysql.cj.jdbc.Driver +log4jdbc.auto.load.popular.drivers=false From d2fe3b69cb7d4815afe7ef1d308ffa5b30740c3f Mon Sep 17 00:00:00 2001 From: jsy3831 Date: Sat, 18 Sep 2021 19:26:21 +0900 Subject: [PATCH 6/8] =?UTF-8?q?#2=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85,?= =?UTF-8?q?=20=ED=83=88=ED=87=B4=20=EC=B6=94=EA=B0=80=20/=20BCrypt=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20/=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../com/photobook/annotation/LoginCheck.java | 2 +- .../com/photobook/aop/AuthCheckAspect.java | 1 - .../com/photobook/config/WebMvcConfig.java | 1 + .../photobook/config/WebSecurityConfig.java | 28 +++++++++ .../photobook/controller/UserController.java | 45 ++++++++++++-- .../LoginUserArgumentResolver.java | 4 +- src/main/java/com/photobook/dto/UserDto.java | 8 +++ .../exception/ExceptionController.java | 42 ++++++++++++-- .../java/com/photobook/mapper/UserMapper.java | 8 ++- .../com/photobook/service/UserService.java | 7 ++- .../service/impl/UserServiceImpl.java | 45 ++++++++++++-- src/main/resources/mapper/UserMapper.xml | 20 +++++-- .../service/impl/UserServiceImplTest.java | 58 ++++++++++--------- .../photobook/utils/PasswordEncoderTest.java | 33 +++++++++++ 15 files changed, 247 insertions(+), 56 deletions(-) create mode 100644 src/main/java/com/photobook/config/WebSecurityConfig.java create mode 100644 src/test/java/com/photobook/utils/PasswordEncoderTest.java diff --git a/build.gradle b/build.gradle index 16d9f4f..fe42113 100644 --- a/build.gradle +++ b/build.gradle @@ -25,6 +25,7 @@ dependencies { implementation 'org.bgee.log4jdbc-log4j2:log4jdbc-log4j2-jdbc4.1:1.16' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-aop' + implementation 'org.springframework.boot:spring-boot-starter-security' // implementation 'org.springframework.boot:spring-boot-starter-data-redis' // implementation 'org.springframework.session:spring-session-data-redis' diff --git a/src/main/java/com/photobook/annotation/LoginCheck.java b/src/main/java/com/photobook/annotation/LoginCheck.java index f82e296..1ff2c1a 100644 --- a/src/main/java/com/photobook/annotation/LoginCheck.java +++ b/src/main/java/com/photobook/annotation/LoginCheck.java @@ -4,6 +4,6 @@ @Documented @Target(ElementType.METHOD) -@Retention(RetentionPolicy.CLASS) +@Retention(RetentionPolicy.RUNTIME) public @interface LoginCheck { } diff --git a/src/main/java/com/photobook/aop/AuthCheckAspect.java b/src/main/java/com/photobook/aop/AuthCheckAspect.java index 006f442..021f177 100644 --- a/src/main/java/com/photobook/aop/AuthCheckAspect.java +++ b/src/main/java/com/photobook/aop/AuthCheckAspect.java @@ -25,7 +25,6 @@ public void loginCheck() { if(userInfo == null) { throw new UnauthorizedException("로그인된 사용자 정보가 존재하지 않습니다."); } - } } diff --git a/src/main/java/com/photobook/config/WebMvcConfig.java b/src/main/java/com/photobook/config/WebMvcConfig.java index 3bff21a..1914b8f 100644 --- a/src/main/java/com/photobook/config/WebMvcConfig.java +++ b/src/main/java/com/photobook/config/WebMvcConfig.java @@ -22,3 +22,4 @@ public void addArgumentResolvers(List resolvers) } } + diff --git a/src/main/java/com/photobook/config/WebSecurityConfig.java b/src/main/java/com/photobook/config/WebSecurityConfig.java new file mode 100644 index 0000000..33f010c --- /dev/null +++ b/src/main/java/com/photobook/config/WebSecurityConfig.java @@ -0,0 +1,28 @@ +package com.photobook.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +@EnableWebSecurity +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + @Bean + public PasswordEncoder getPasswordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.cors().disable() + .csrf().disable() + .formLogin().disable() + .headers().frameOptions().disable(); + } + +} diff --git a/src/main/java/com/photobook/controller/UserController.java b/src/main/java/com/photobook/controller/UserController.java index b2a9909..7fed4af 100644 --- a/src/main/java/com/photobook/controller/UserController.java +++ b/src/main/java/com/photobook/controller/UserController.java @@ -7,9 +7,11 @@ import com.photobook.exception.customException.DuplicatedException; import com.photobook.service.LoginService; import com.photobook.service.UserService; +import org.springframework.http.HttpStatus; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; import javax.validation.constraints.NotBlank; @RestController @@ -27,13 +29,14 @@ public UserController(UserService userService, LoginService loginService) { } @PostMapping("/login") - public Response login(@RequestParam @NotBlank String id, @RequestParam @NotBlank String password) { + public Response login(@RequestParam @NotBlank String id, @RequestParam @NotBlank String password, + @LoginUser UserDto loginUser) { - if(loginService.getLoginUserInfo() != null) { + if(loginUser != null) { throw new DuplicatedException("이미 로그인된 상태입니다."); } - UserDto userInfo = userService.getUserInfoByIdAndPassword(id, password); + UserDto userInfo = userService.validateLogin(id, password); loginService.setLoginUserInfo(userInfo); @@ -42,7 +45,8 @@ public Response login(@RequestParam @NotBlank String id, @RequestParam @PostMapping("/logout") @LoginCheck - public Response logout() { + public Response logout() { + loginService.removeLoginUserInfo(); return Response.toResponse(200, "로그아웃 하였습니다."); @@ -50,11 +54,40 @@ public Response logout() { @GetMapping("/my-info") @LoginCheck - public Response getUserInfoById(@LoginUser String id) { + public Response getUserInfoById(@LoginUser UserDto loginUser) { - UserDto userInfo = userService.getUserInfoById(id); + UserDto userInfo = userService.getUserInfoById(loginUser.getId()); return Response.toResponse(200, "회원정보 조회", userInfo); } + @PostMapping("/signup") + @ResponseStatus(HttpStatus.CREATED) + public Response signup(@RequestBody @Valid UserDto userInfo) { + + userService.createUser(userInfo); + + return Response.toResponse(201, "회원가입 하였습니다."); + } + + @GetMapping("/{id}") + public Response checkUserId(@PathVariable @NotBlank String id) { + + userService.validateUserId(id); + + return Response.toResponse(200, "사용가능한 아이디입니다."); + } + + + @DeleteMapping("/my-info") + @LoginCheck + public Response deleteUser(@LoginUser UserDto loginUser) { + + userService.deleteUser(loginUser.getId()); + + loginService.removeLoginUserInfo(); + + return Response.toResponse(200, "회원탈퇴 하였습니다."); + } + } \ No newline at end of file diff --git a/src/main/java/com/photobook/controller/argumentResolver/LoginUserArgumentResolver.java b/src/main/java/com/photobook/controller/argumentResolver/LoginUserArgumentResolver.java index feb8671..a357bf3 100644 --- a/src/main/java/com/photobook/controller/argumentResolver/LoginUserArgumentResolver.java +++ b/src/main/java/com/photobook/controller/argumentResolver/LoginUserArgumentResolver.java @@ -25,7 +25,7 @@ public boolean supportsParameter(MethodParameter parameter) { @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { - return loginService.getLoginUserInfo().getId(); + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + return loginService.getLoginUserInfo(); } } diff --git a/src/main/java/com/photobook/dto/UserDto.java b/src/main/java/com/photobook/dto/UserDto.java index 715e9b9..4df1296 100644 --- a/src/main/java/com/photobook/dto/UserDto.java +++ b/src/main/java/com/photobook/dto/UserDto.java @@ -4,6 +4,7 @@ import lombok.Setter; import lombok.ToString; +import javax.validation.constraints.*; import java.io.Serializable; import java.time.LocalDate; @@ -14,14 +15,21 @@ public class UserDto implements Serializable { private int userId; + @NotBlank private String id; + @NotBlank private String password; + @NotBlank private String name; + @NotBlank + @Email private String email; + @NotNull + @Past private LocalDate birth; private String profileImageName; diff --git a/src/main/java/com/photobook/exception/ExceptionController.java b/src/main/java/com/photobook/exception/ExceptionController.java index 5456fd6..ae9994d 100644 --- a/src/main/java/com/photobook/exception/ExceptionController.java +++ b/src/main/java/com/photobook/exception/ExceptionController.java @@ -5,11 +5,17 @@ import com.photobook.exception.customException.UnauthorizedException; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; +import org.springframework.security.core.AuthenticationException; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.validation.ConstraintViolationException; +import java.util.ArrayList; +import java.util.List; @Slf4j @RestControllerAdvice @@ -21,35 +27,61 @@ private static ExceptionType exceptionType(Exception e) { @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.BAD_REQUEST) - public Response handleException(Exception e) { + public Response handleException(Exception e) { log.error("handleException throw Exception : {}", e.getMessage()); return Response.toResponse(400, e.getMessage(), exceptionType(e)); } @ExceptionHandler(ConstraintViolationException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) - public Response handleConstraintViolationException(ConstraintViolationException e) { + public Response handleConstraintViolationException(ConstraintViolationException e) { log.error("handleConstraintViolationException throw ConstraintViolationException : {}", e.getMessage()); return Response.toResponse(400, e.getMessage(), exceptionType(e)); } @ExceptionHandler(IllegalArgumentException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) - public Response handleIllegalArgumentException(IllegalArgumentException e) { + public Response handleIllegalArgumentException(IllegalArgumentException e) { log.error("handleIllegalArgumentException throw IllegalArgumentException : {}", e.getMessage()); return Response.toResponse(400, e.getMessage(), exceptionType(e)); } @ExceptionHandler(UnauthorizedException.class) @ResponseStatus(HttpStatus.UNAUTHORIZED) - public Response handleUnauthorizedException(UnauthorizedException e) { + public Response handleUnauthorizedException(UnauthorizedException e) { return Response.toResponse(401, e.getMessage(), exceptionType(e)); } @ExceptionHandler(DuplicatedException.class) @ResponseStatus(HttpStatus.CONFLICT) - public Response handleDuplicatedException(DuplicatedException e) { + public Response handleDuplicatedException(DuplicatedException e) { return Response.toResponse(409, e.getMessage(), exceptionType(e)); } + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Response handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { + + BindingResult bindingResult = e.getBindingResult(); + List message = new ArrayList<>(); + + for(FieldError fieldError : bindingResult.getFieldErrors()) { + StringBuilder builder = new StringBuilder(); + + builder.append(fieldError.getField()); + builder.append(" : "); + builder.append(fieldError.getDefaultMessage()); + + message.add(builder); + } + + return Response.toResponse(400, message.toString(), exceptionType(e)); + } + + @ExceptionHandler(AuthenticationException.class) + @ResponseStatus(HttpStatus.FORBIDDEN) + public Response handleAuthenticationException(AuthenticationException e) { + return Response.toResponse(403, e.getMessage(), exceptionType(e)); + } + } diff --git a/src/main/java/com/photobook/mapper/UserMapper.java b/src/main/java/com/photobook/mapper/UserMapper.java index 3d7c608..8e67368 100644 --- a/src/main/java/com/photobook/mapper/UserMapper.java +++ b/src/main/java/com/photobook/mapper/UserMapper.java @@ -7,8 +7,12 @@ @Mapper public interface UserMapper { - UserDto getUserInfoByIdAndPassword(@Param("id") String id, @Param("password") String password); - UserDto getUserInfoById(@Param("id") String id); + void createUser(UserDto userDto); + + boolean checkUserId(@Param("id") String id); + + void deleteUser(String id); + } diff --git a/src/main/java/com/photobook/service/UserService.java b/src/main/java/com/photobook/service/UserService.java index bfb4a65..998f652 100644 --- a/src/main/java/com/photobook/service/UserService.java +++ b/src/main/java/com/photobook/service/UserService.java @@ -4,8 +4,13 @@ public interface UserService { - UserDto getUserInfoByIdAndPassword(String id, String password); + UserDto validateLogin(String id, String password); UserDto getUserInfoById(String id); + void createUser(UserDto userInfo); + + void validateUserId(String id); + + void deleteUser(String id); } diff --git a/src/main/java/com/photobook/service/impl/UserServiceImpl.java b/src/main/java/com/photobook/service/impl/UserServiceImpl.java index f536709..a95ea2f 100644 --- a/src/main/java/com/photobook/service/impl/UserServiceImpl.java +++ b/src/main/java/com/photobook/service/impl/UserServiceImpl.java @@ -1,26 +1,39 @@ package com.photobook.service.impl; +import com.photobook.exception.customException.DuplicatedException; import com.photobook.mapper.UserMapper; import com.photobook.dto.UserDto; import com.photobook.service.UserService; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; -import org.springframework.util.Assert; @Service public class UserServiceImpl implements UserService { private final UserMapper userMapper; + private final PasswordEncoder passwordEncoder; - public UserServiceImpl(UserMapper userMapper) { + public UserServiceImpl(UserMapper userMapper, PasswordEncoder passwordEncoder) { this.userMapper = userMapper; + this.passwordEncoder = passwordEncoder; } @Override - public UserDto getUserInfoByIdAndPassword(String id, String password) { + public UserDto validateLogin(String id, String password) { - UserDto userInfo = userMapper.getUserInfoByIdAndPassword(id, password); + UserDto userInfo = userMapper.getUserInfoById(id); + + if(userInfo == null) { + throw new InternalAuthenticationServiceException("존재하지않는 아이디입니다."); + } - Assert.notNull(userInfo, "아이디 또는 비밀번호가 잘못 입력 되었습니다."); + boolean isPwdMatch = passwordEncoder.matches(password, userInfo.getPassword()); + + if(!isPwdMatch) { + throw new BadCredentialsException("일치하지않는 비밀번호입니다."); + } return userInfo; } @@ -30,4 +43,26 @@ public UserDto getUserInfoById(String id) { UserDto userInfo = userMapper.getUserInfoById(id); return userInfo; } + + @Override + public void createUser(UserDto userInfo) { + validateUserId(userInfo.getId()); + + String encodedPwd = passwordEncoder.encode(userInfo.getPassword()); + userInfo.setPassword(encodedPwd); + + userMapper.createUser(userInfo); + } + + @Override + public void validateUserId(String id) { + if(userMapper.checkUserId(id)) { + throw new DuplicatedException("중복된 아이디입니다."); + } + } + + @Override + public void deleteUser(String id) { + userMapper.deleteUser(id); + } } diff --git a/src/main/resources/mapper/UserMapper.xml b/src/main/resources/mapper/UserMapper.xml index fc214ad..2f496f7 100644 --- a/src/main/resources/mapper/UserMapper.xml +++ b/src/main/resources/mapper/UserMapper.xml @@ -15,17 +15,25 @@ - SELECT user_id, id, password, name, email, birth, profile_image_name, profile_image_path, profile_message FROM user WHERE id = #{id} - AND password = #{password} - + SELECT EXISTS + (SELECT id FROM user WHERE id = #{id}) as exist + + DELETE FROM user + WHERE id = #{id} + + \ No newline at end of file diff --git a/src/test/java/com/photobook/service/impl/UserServiceImplTest.java b/src/test/java/com/photobook/service/impl/UserServiceImplTest.java index f86db93..89c0e50 100644 --- a/src/test/java/com/photobook/service/impl/UserServiceImplTest.java +++ b/src/test/java/com/photobook/service/impl/UserServiceImplTest.java @@ -8,6 +8,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.crypto.password.PasswordEncoder; import java.time.LocalDate; @@ -24,6 +25,9 @@ class UserServiceImplTest { @Mock UserMapper userMapper; + @Mock + PasswordEncoder passwordEncoder; + public UserDto userInfo() { UserDto userInfo = new UserDto(); userInfo.setUserId(1); @@ -39,31 +43,31 @@ public UserDto userInfo() { return userInfo; } - @Test - @DisplayName("사용자 정보가 존재하면 성공") - public void successGetUserInfoByIdAndPassword() { - //given - UserDto userInfo = userInfo(); - when(userMapper.getUserInfoByIdAndPassword("admin", "1234")).thenReturn(userInfo); - - //when - UserDto result = userServiceImpl.getUserInfoByIdAndPassword("admin", "1234"); - - //then - assertEquals(result, userInfo); - } - - @Test - @DisplayName("사용자 정보가 존재하지 않으면 성공") - public void failureGetUserInfoByIdAndPassword() { - //given - when(userMapper.getUserInfoByIdAndPassword("notExistId", "notExistPassword")).thenReturn(null); - - //then - assertThrows(IllegalArgumentException.class, () -> { - //when - userServiceImpl.getUserInfoByIdAndPassword("notExistId", "notExistPassword"); - }); - - } +// @Test +// @DisplayName("사용자 정보가 존재하면 성공") +// public void successGetUserInfoByIdAndPassword() { +// //given +// UserDto userInfo = userInfo(); +// when(userMapper.getUserInfoByIdAndPassword("admin", "1234")).thenReturn(userInfo); +// +// //when +// UserDto result = userServiceImpl.getUserInfoByIdAndPassword("admin", "1234"); +// +// //then +// assertEquals(result, userInfo); +// } +// +// @Test +// @DisplayName("사용자 정보가 존재하지 않으면 성공") +// public void failureGetUserInfoByIdAndPassword() { +// //given +// when(userMapper.getUserInfoByIdAndPassword("notExistId", "notExistPassword")).thenReturn(null); +// +// //then +// assertThrows(IllegalArgumentException.class, () -> { +// //when +// userServiceImpl.getUserInfoByIdAndPassword("notExistId", "notExistPassword"); +// }); +// +// } } \ No newline at end of file diff --git a/src/test/java/com/photobook/utils/PasswordEncoderTest.java b/src/test/java/com/photobook/utils/PasswordEncoderTest.java new file mode 100644 index 0000000..698c761 --- /dev/null +++ b/src/test/java/com/photobook/utils/PasswordEncoderTest.java @@ -0,0 +1,33 @@ +package com.photobook.utils; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.crypto.password.PasswordEncoder; + +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +class PasswordEncoderTest { + + @Autowired + private PasswordEncoder passwordEncoder; + + @Test + @DisplayName("패스워드 암호화 테스트") + void passwordEncode() { + //given + String rawPwd = "1234"; + + //when + String encodedPwd = passwordEncoder.encode(rawPwd); + + //then + assertAll( + () -> assertNotEquals(rawPwd, encodedPwd), + () -> assertTrue(passwordEncoder.matches(rawPwd, encodedPwd)) + ); + } + +} From 0caccc1679c00e0a659dc155a15b9d2ade02d8be Mon Sep 17 00:00:00 2001 From: jsy3831 Date: Sat, 18 Sep 2021 19:57:53 +0900 Subject: [PATCH 7/8] =?UTF-8?q?#2=20ehcache=20=EC=9E=84=EC=8B=9C=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../com/photobook/PhotobookApplication.java | 2 ++ .../service/impl/UserServiceImpl.java | 2 ++ src/main/resources/ehcache.xml | 18 ++++++++++++++++++ 4 files changed, 23 insertions(+) create mode 100644 src/main/resources/ehcache.xml diff --git a/build.gradle b/build.gradle index fe42113..88b4502 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-aop' implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-cache' // implementation 'org.springframework.boot:spring-boot-starter-data-redis' // implementation 'org.springframework.session:spring-session-data-redis' diff --git a/src/main/java/com/photobook/PhotobookApplication.java b/src/main/java/com/photobook/PhotobookApplication.java index 012ee43..faa3796 100644 --- a/src/main/java/com/photobook/PhotobookApplication.java +++ b/src/main/java/com/photobook/PhotobookApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; @SpringBootApplication +@EnableCaching public class PhotobookApplication { public static void main(String[] args) { diff --git a/src/main/java/com/photobook/service/impl/UserServiceImpl.java b/src/main/java/com/photobook/service/impl/UserServiceImpl.java index a95ea2f..f2d73b9 100644 --- a/src/main/java/com/photobook/service/impl/UserServiceImpl.java +++ b/src/main/java/com/photobook/service/impl/UserServiceImpl.java @@ -4,6 +4,7 @@ import com.photobook.mapper.UserMapper; import com.photobook.dto.UserDto; import com.photobook.service.UserService; +import org.springframework.cache.annotation.Cacheable; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.crypto.password.PasswordEncoder; @@ -39,6 +40,7 @@ public UserDto validateLogin(String id, String password) { } @Override + @Cacheable(value="findUserCache", key="#id") public UserDto getUserInfoById(String id) { UserDto userInfo = userMapper.getUserInfoById(id); return userInfo; diff --git a/src/main/resources/ehcache.xml b/src/main/resources/ehcache.xml new file mode 100644 index 0000000..bbae485 --- /dev/null +++ b/src/main/resources/ehcache.xml @@ -0,0 +1,18 @@ + + + + + + + + + From 60e972184f9a3839f8cd586845cd6c7b7d8dee0c Mon Sep 17 00:00:00 2001 From: jsy3831 Date: Sat, 18 Sep 2021 22:05:16 +0900 Subject: [PATCH 8/8] =?UTF-8?q?#2=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/UserServiceImplTest.java | 75 ++++++++++++------- .../photobook/utils/PasswordEncoderTest.java | 1 + 2 files changed, 48 insertions(+), 28 deletions(-) diff --git a/src/test/java/com/photobook/service/impl/UserServiceImplTest.java b/src/test/java/com/photobook/service/impl/UserServiceImplTest.java index 89c0e50..4d045f7 100644 --- a/src/test/java/com/photobook/service/impl/UserServiceImplTest.java +++ b/src/test/java/com/photobook/service/impl/UserServiceImplTest.java @@ -8,6 +8,8 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.InternalAuthenticationServiceException; import org.springframework.security.crypto.password.PasswordEncoder; import java.time.LocalDate; @@ -32,7 +34,7 @@ public UserDto userInfo() { UserDto userInfo = new UserDto(); userInfo.setUserId(1); userInfo.setId("admin"); - userInfo.setPassword("1234"); + userInfo.setPassword(passwordEncoder.encode("1234")); userInfo.setName("관리자"); userInfo.setEmail("admin@gmail.com"); userInfo.setBirth(LocalDate.of(1993, 10, 28)); @@ -43,31 +45,48 @@ public UserDto userInfo() { return userInfo; } -// @Test -// @DisplayName("사용자 정보가 존재하면 성공") -// public void successGetUserInfoByIdAndPassword() { -// //given -// UserDto userInfo = userInfo(); -// when(userMapper.getUserInfoByIdAndPassword("admin", "1234")).thenReturn(userInfo); -// -// //when -// UserDto result = userServiceImpl.getUserInfoByIdAndPassword("admin", "1234"); -// -// //then -// assertEquals(result, userInfo); -// } -// -// @Test -// @DisplayName("사용자 정보가 존재하지 않으면 성공") -// public void failureGetUserInfoByIdAndPassword() { -// //given -// when(userMapper.getUserInfoByIdAndPassword("notExistId", "notExistPassword")).thenReturn(null); -// -// //then -// assertThrows(IllegalArgumentException.class, () -> { -// //when -// userServiceImpl.getUserInfoByIdAndPassword("notExistId", "notExistPassword"); -// }); -// -// } + @Test + @DisplayName("로그인 검증 성공") + public void successValidateLogin() { + //given + UserDto userInfo = userInfo(); + when(userMapper.getUserInfoById("admin")).thenReturn(userInfo); + when(passwordEncoder.matches("1234", userInfo.getPassword())).thenReturn(true); + + //when + UserDto result = userServiceImpl.validateLogin("admin", "1234"); + + //then + assertEquals(result, userInfo); + } + + @Test + @DisplayName("로그인 검증 실패_존재하지 않는 아이디") + public void failureValidateLoginId() { + //given + when(userMapper.getUserInfoById("admin")).thenReturn(null); + + //then + assertThrows(InternalAuthenticationServiceException.class, () -> { + //when + userServiceImpl.validateLogin("admin", "1234"); + }); + + } + + @Test + @DisplayName("로그인 검증 실패_비밀번호 불일치") + public void failureValidateLoginPwd() { + //given + UserDto userInfo = userInfo(); + when(userMapper.getUserInfoById("admin")).thenReturn(userInfo); + when(passwordEncoder.matches("1234", userInfo.getPassword())).thenReturn(false); + + //then + assertThrows(BadCredentialsException.class, () -> { + //when + userServiceImpl.validateLogin("admin", "1234"); + }); + } + } \ No newline at end of file diff --git a/src/test/java/com/photobook/utils/PasswordEncoderTest.java b/src/test/java/com/photobook/utils/PasswordEncoderTest.java index 698c761..267966d 100644 --- a/src/test/java/com/photobook/utils/PasswordEncoderTest.java +++ b/src/test/java/com/photobook/utils/PasswordEncoderTest.java @@ -22,6 +22,7 @@ void passwordEncode() { //when String encodedPwd = passwordEncoder.encode(rawPwd); + System.out.println(encodedPwd); //then assertAll(