From e8560f3cb29cc219a9161520ae9ff50ebfd2c9ae Mon Sep 17 00:00:00 2001 From: yeeeeerim Date: Mon, 10 Apr 2023 13:03:56 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85,=20?= =?UTF-8?q?=EC=A0=9C=ED=92=88=20crud?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../metamall/annotation/PermissionCheck.java | 12 +++ .../controller/ProductController.java | 91 +++++++++++++++++++ .../metamall/controller/UserController.java | 21 +++++ .../core/advice/AuthorizationAspect.java | 18 ++++ .../metamall/dto/product/ProductRequest.java | 23 +++++ .../metamall/dto/product/ProductResponse.java | 4 + .../metamall/dto/user/UserRequest.java | 13 +++ .../metamall/model/log/error/ErrorLog.java | 1 + .../model/orderproduct/OrderProduct.java | 3 +- .../metamall/model/ordersheet/OrderSheet.java | 5 +- .../metamall/model/product/Product.java | 7 ++ .../model/product/ProductRepository.java | 4 + .../mtcoding/metamall/model/user/User.java | 28 ++++-- .../controller/UserControllerTest.java | 28 ++++++ 14 files changed, 245 insertions(+), 13 deletions(-) create mode 100644 src/main/java/shop/mtcoding/metamall/annotation/PermissionCheck.java create mode 100644 src/main/java/shop/mtcoding/metamall/controller/ProductController.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/advice/AuthorizationAspect.java create mode 100644 src/main/java/shop/mtcoding/metamall/dto/product/ProductRequest.java create mode 100644 src/main/java/shop/mtcoding/metamall/dto/product/ProductResponse.java create mode 100644 src/test/java/shop/mtcoding/metamall/controller/UserControllerTest.java diff --git a/src/main/java/shop/mtcoding/metamall/annotation/PermissionCheck.java b/src/main/java/shop/mtcoding/metamall/annotation/PermissionCheck.java new file mode 100644 index 0000000..2f626d1 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/annotation/PermissionCheck.java @@ -0,0 +1,12 @@ +package shop.mtcoding.metamall.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface PermissionCheck { + String[] roles() default {}; +} diff --git a/src/main/java/shop/mtcoding/metamall/controller/ProductController.java b/src/main/java/shop/mtcoding/metamall/controller/ProductController.java new file mode 100644 index 0000000..7517b9a --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/controller/ProductController.java @@ -0,0 +1,91 @@ +package shop.mtcoding.metamall.controller; + +import lombok.RequiredArgsConstructor; +import org.apache.coyote.Response; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import shop.mtcoding.metamall.core.exception.Exception400; +import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.dto.product.ProductRequest; +import shop.mtcoding.metamall.model.product.Product; +import shop.mtcoding.metamall.model.product.ProductRepository; +import shop.mtcoding.metamall.model.user.UserRepository; + +import javax.servlet.http.HttpServletRequest; +import javax.transaction.Transactional; +import java.util.List; +import java.util.Optional; + +@RequiredArgsConstructor +@RestController +public class ProductController { + + private final ProductRepository productRepository; + private final UserRepository userRepository; + + @PostMapping("/products") + public ResponseEntity productsSave(@RequestBody ProductRequest.ProductSaveDto productDto, HttpServletRequest request ){ + Optional productOP = productRepository.findByName(productDto.getName()); + if(!productOP.isPresent()){ + Product product = Product.builder() + .name(productDto.getName()) + .price(productDto.getPrice()) + .qty(productDto.getQty()) + .build(); + productRepository.save(product); + ResponseDto responseDto = new ResponseDto<>().data(product); + return ResponseEntity.ok().body(responseDto); + }else{ + throw new Exception400 ("중복된 상품명이 있습니다."); + } + } + + @GetMapping("/products") + public ResponseEntity productList(){ + List productList=productRepository.findAll(); + ResponseDto responseDto=new ResponseDto<>().data(productList); + return ResponseEntity.ok().body(responseDto); + } + + @GetMapping("/products/{name}") + public ResponseEntity productDetail(@PathVariable String name){ + Optional product=productRepository.findByName(name); + if(product.isPresent()){ + ResponseDto responseDto = new ResponseDto<>().data(product); + return ResponseEntity.ok().body(responseDto); + }else{ + throw new Exception400 ("존재하지 않는 상품입니다. 상품명을 다시 입력하세요 "); + } + } + + /*** + * + * @param productDto + * @return 상품수정 + */ + @PutMapping("/products/{name}") + public ResponseEntity productUpdate(@RequestBody ProductRequest.ProductUpdateDto productDto){ + Optionalproduct=productRepository.findByName(productDto.getName()); + if(product.isPresent()){ + product.get().updateProduct(productDto); + productRepository.save(product.get()); + ResponseDto responseDto = new ResponseDto<>().data(product); + return ResponseEntity.ok().body(responseDto); + }else{ + throw new Exception400 ("존재하지 않는 상품입니다. 상품명을 다시 입력하세요 "); + } + } + + @Transactional + @DeleteMapping("/products/{name}") + public ResponseEntity productDelete(@PathVariable String name){ + productRepository.deleteByName(name); + ResponseDtoresponseDto=new ResponseDto<>().data("삭제완료"); + return ResponseEntity.ok().body(responseDto); + + } + + + + +} diff --git a/src/main/java/shop/mtcoding/metamall/controller/UserController.java b/src/main/java/shop/mtcoding/metamall/controller/UserController.java index ddfee94..a4322f4 100644 --- a/src/main/java/shop/mtcoding/metamall/controller/UserController.java +++ b/src/main/java/shop/mtcoding/metamall/controller/UserController.java @@ -60,4 +60,25 @@ public ResponseEntity login(@RequestBody UserRequest.LoginDto loginDto, HttpS throw new Exception400("유저네임 혹은 아이디가 잘못되었습니다"); } } + + /** + * 회원가입 : joinDTO를 받아 Username이 존재하지 않을경우 회원가입 처리 + * */ + @PostMapping("/join") + public ResponseEntity join (@RequestBody UserRequest.JoinDto joinDto, HttpServletRequest request) { + Optional userOP = userRepository.findByUsername(joinDto.getUsername()); // + if(!userOP.isPresent()){ + User user = User.builder().username(joinDto.getUsername()).password(joinDto.getPassword()).email(joinDto.getEmail()).role(joinDto.getRole()).build(); + userRepository.save(user); + ResponseDto responseDto = new ResponseDto<>().data(user); + return ResponseEntity.ok().body(responseDto); + }else { + throw new Exception400("중복된 유저네임 아이디입니다."); + } + + + } + + + } diff --git a/src/main/java/shop/mtcoding/metamall/core/advice/AuthorizationAspect.java b/src/main/java/shop/mtcoding/metamall/core/advice/AuthorizationAspect.java new file mode 100644 index 0000000..002a13a --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/advice/AuthorizationAspect.java @@ -0,0 +1,18 @@ +package shop.mtcoding.metamall.core.advice; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; + +@Aspect +@Component +public class AuthorizationAspect { + + @Before("@annotation(shop.mtcoding.metamall.annotation.PermissionCheck)") + public void checkAuthorization(JoinPoint joinPoint) { + // 권한 체크 로직 구현 + // 로그인한 사용자의 권한 정보와 메소드에 설정된 권한 정보를 비교하여 인가 여부 판단 + // 인가되지 않은 경우 예외 처리 등 필요한 작업 수행 + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/dto/product/ProductRequest.java b/src/main/java/shop/mtcoding/metamall/dto/product/ProductRequest.java new file mode 100644 index 0000000..e315e81 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/dto/product/ProductRequest.java @@ -0,0 +1,23 @@ +package shop.mtcoding.metamall.dto.product; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +public class ProductRequest { + @Getter + @Setter + public static class ProductSaveDto{ + private String name; + private Integer price; + private Integer qty; + } + @Getter + @Setter + public static class ProductUpdateDto{ + private String name; + private Integer price; + private Integer qty; + } +} diff --git a/src/main/java/shop/mtcoding/metamall/dto/product/ProductResponse.java b/src/main/java/shop/mtcoding/metamall/dto/product/ProductResponse.java new file mode 100644 index 0000000..11d3e67 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/dto/product/ProductResponse.java @@ -0,0 +1,4 @@ +package shop.mtcoding.metamall.dto.product; + +public class ProductResponse { +} diff --git a/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java b/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java index 80947db..c82d916 100644 --- a/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java +++ b/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java @@ -2,6 +2,9 @@ import lombok.Getter; import lombok.Setter; +import shop.mtcoding.metamall.model.user.User; + +import java.time.LocalDateTime; public class UserRequest { @Getter @Setter @@ -9,4 +12,14 @@ public static class LoginDto { private String username; private String password; } + @Getter + @Setter + public static class JoinDto{ + private String username; + private String password; + private String email; + private String role; // USER(고객), SELLER(판매자), ADMIN(관리자) + + + } } diff --git a/src/main/java/shop/mtcoding/metamall/model/log/error/ErrorLog.java b/src/main/java/shop/mtcoding/metamall/model/log/error/ErrorLog.java index fbfe7e5..a7279e7 100644 --- a/src/main/java/shop/mtcoding/metamall/model/log/error/ErrorLog.java +++ b/src/main/java/shop/mtcoding/metamall/model/log/error/ErrorLog.java @@ -14,6 +14,7 @@ @Table(name = "error_log_tb") @Entity public class ErrorLog { + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; diff --git a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java b/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java index 165905e..9209aa5 100644 --- a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java +++ b/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java @@ -26,7 +26,8 @@ public class OrderProduct { // 주문 상품 private LocalDateTime createdAt; private LocalDateTime updatedAt; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name="sheet_id") private OrderSheet orderSheet; @PrePersist diff --git a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java index 7638710..1827add 100644 --- a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java +++ b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java @@ -22,9 +22,10 @@ public class OrderSheet { // 주문서 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name="user_id") private User user; // 주문자 - @OneToMany(mappedBy = "orderSheet") + @OneToMany(cascade = CascadeType.ALL, mappedBy ="orderSheet", fetch = FetchType.LAZY) private List orderProductList = new ArrayList<>(); // 총 주문 상품 리스트 private Integer totalPrice; // 총 주문 금액 (총 주문 상품 리스트의 orderPrice 합) private LocalDateTime createdAt; diff --git a/src/main/java/shop/mtcoding/metamall/model/product/Product.java b/src/main/java/shop/mtcoding/metamall/model/product/Product.java index bc8c618..f76b29d 100644 --- a/src/main/java/shop/mtcoding/metamall/model/product/Product.java +++ b/src/main/java/shop/mtcoding/metamall/model/product/Product.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import shop.mtcoding.metamall.dto.product.ProductRequest; import javax.persistence.*; import java.time.LocalDateTime; @@ -42,4 +43,10 @@ public Product(Long id, String name, Integer price, Integer qty, LocalDateTime c this.createdAt = createdAt; this.updatedAt = updatedAt; } + public void updateProduct(ProductRequest.ProductUpdateDto productUpdateDto){ + this.name= productUpdateDto.getName(); + this.qty=productUpdateDto.getQty(); + this.price=productUpdateDto.getPrice(); + } + } diff --git a/src/main/java/shop/mtcoding/metamall/model/product/ProductRepository.java b/src/main/java/shop/mtcoding/metamall/model/product/ProductRepository.java index ba5def3..234e223 100644 --- a/src/main/java/shop/mtcoding/metamall/model/product/ProductRepository.java +++ b/src/main/java/shop/mtcoding/metamall/model/product/ProductRepository.java @@ -2,5 +2,9 @@ import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface ProductRepository extends JpaRepository { + Optional findByName(String name); + void deleteByName(String name); } diff --git a/src/main/java/shop/mtcoding/metamall/model/user/User.java b/src/main/java/shop/mtcoding/metamall/model/user/User.java index c929ce5..0c172e0 100644 --- a/src/main/java/shop/mtcoding/metamall/model/user/User.java +++ b/src/main/java/shop/mtcoding/metamall/model/user/User.java @@ -1,35 +1,43 @@ package shop.mtcoding.metamall.model.user; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.*; +import shop.mtcoding.metamall.model.ordersheet.OrderSheet; import javax.persistence.*; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; -@NoArgsConstructor -@Setter // DTO 만들면 삭제해야됨 -@Getter -@Table(name = "user_tb") @Entity +@Builder +@Getter +@NoArgsConstructor +@ToString +@Setter +@AllArgsConstructor +@Table(name="user_tb") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; + @JsonIgnore private String password; private String email; private String role; // USER(고객), SELLER(판매자), ADMIN(관리자) private LocalDateTime createdAt; private LocalDateTime updatedAt; - @PrePersist + @OneToMany(cascade = CascadeType.ALL, mappedBy ="user", fetch = FetchType.LAZY) + private List orderSheet = new ArrayList<>(); + + @PrePersist //insert직전에 발동 protected void onCreate() { this.createdAt = LocalDateTime.now(); } - @PreUpdate + @PreUpdate //update할때 발동 protected void onUpdate() { this.updatedAt = LocalDateTime.now(); } diff --git a/src/test/java/shop/mtcoding/metamall/controller/UserControllerTest.java b/src/test/java/shop/mtcoding/metamall/controller/UserControllerTest.java new file mode 100644 index 0000000..47f3bc0 --- /dev/null +++ b/src/test/java/shop/mtcoding/metamall/controller/UserControllerTest.java @@ -0,0 +1,28 @@ +package shop.mtcoding.metamall.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; + +import static org.junit.jupiter.api.Assertions.*; +import static org.springframework.http.RequestEntity.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest +class UserControllerTest { + @Autowired + private MockMvc mockMvc; + @Test + void login() { + + } + + @Test + void join() { + + } +} \ No newline at end of file From cb766ffdd3cb155b87ab1e27c0d5c0084f6116fb Mon Sep 17 00:00:00 2001 From: yeeeeerim Date: Fri, 14 Apr 2023 21:26:50 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=ED=86=A0=EC=9D=B4=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EC=A0=9D=ED=8A=B8=20=EC=99=84=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 + .../metamall/config/FilterRegisterConfig.java | 9 +- .../metamall/config/MyWebMvcConfig.java | 49 ++++++ .../metamall/config/WebMvcConfig.java | 18 --- .../metamall/controller/AdminController.java | 35 +++++ .../metamall/controller/OrderController.java | 144 ++++++++++++++++++ .../controller/ProductController.java | 126 +++++++-------- .../metamall/controller/UserController.java | 100 ++++++------ .../core/advice/AuthorizationAspect.java | 18 --- .../core/advice/MyErrorLogAdvice.java | 42 +++++ .../core/advice/MyExceptionAdvice.java | 37 +++-- .../core/advice/MySameUserIdAdvice.java | 48 ++++++ .../metamall/core/advice/MyValidAdvice.java | 38 +++++ .../core/annotation/MyErrorLogRecord.java | 11 ++ .../core/annotation/MySameUserIdCheck.java | 11 ++ .../core/annotation/MySessionStore.java | 11 ++ .../metamall/core/exception/Exception400.java | 21 ++- .../metamall/core/exception/Exception401.java | 6 +- .../metamall/core/exception/Exception403.java | 6 +- .../metamall/core/exception/Exception404.java | 24 --- .../metamall/core/exception/Exception500.java | 24 --- .../metamall/core/filter/JwtVerifyFilter.java | 15 +- .../core/interceptor/MyAdminInterceptor.java | 24 +++ .../core/interceptor/MySellerInterceptor.java | 26 ++++ .../resolver/MySessionArgumentResolver.java | 32 ++++ .../{LoginUser.java => SessionUser.java} | 6 +- .../metamall/core/util/MyDateUtil.java | 10 ++ .../core/util/MyFilterResponseUtil.java | 40 +++++ .../{ResponseDto.java => ResponseDTO.java} | 8 +- .../shop/mtcoding/metamall/dto/ValidDTO.java | 13 ++ .../metamall/dto/order/OrderRequest.java | 46 ++++++ .../metamall/dto/product/ProductRequest.java | 45 +++++- .../metamall/dto/product/ProductResponse.java | 4 - .../metamall/dto/user/UserRequest.java | 52 +++++-- .../metamall/model/log/error/ErrorLog.java | 1 - .../product}/OrderProduct.java | 16 +- .../product}/OrderProductRepository.java | 2 +- .../sheet}/OrderSheet.java | 24 ++- .../order/sheet/OrderSheetRepository.java | 12 ++ .../ordersheet/OrderSheetRepository.java | 6 - .../metamall/model/product/Product.java | 37 ++++- .../model/product/ProductRepository.java | 4 - .../mtcoding/metamall/model/user/User.java | 51 ++++--- .../metamall/model/user/UserRepository.java | 2 +- .../shop/mtcoding/metamall/util/JwtUtil.java | 34 +++++ src/main/resources/application.yml | 4 +- 46 files changed, 991 insertions(+), 303 deletions(-) create mode 100644 src/main/java/shop/mtcoding/metamall/config/MyWebMvcConfig.java delete mode 100644 src/main/java/shop/mtcoding/metamall/config/WebMvcConfig.java create mode 100644 src/main/java/shop/mtcoding/metamall/controller/AdminController.java create mode 100644 src/main/java/shop/mtcoding/metamall/controller/OrderController.java delete mode 100644 src/main/java/shop/mtcoding/metamall/core/advice/AuthorizationAspect.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/advice/MyErrorLogAdvice.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/advice/MySameUserIdAdvice.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/advice/MyValidAdvice.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/annotation/MyErrorLogRecord.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/annotation/MySameUserIdCheck.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/annotation/MySessionStore.java delete mode 100644 src/main/java/shop/mtcoding/metamall/core/exception/Exception404.java delete mode 100644 src/main/java/shop/mtcoding/metamall/core/exception/Exception500.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/interceptor/MyAdminInterceptor.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/interceptor/MySellerInterceptor.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/resolver/MySessionArgumentResolver.java rename src/main/java/shop/mtcoding/metamall/core/session/{LoginUser.java => SessionUser.java} (66%) create mode 100644 src/main/java/shop/mtcoding/metamall/core/util/MyDateUtil.java create mode 100644 src/main/java/shop/mtcoding/metamall/core/util/MyFilterResponseUtil.java rename src/main/java/shop/mtcoding/metamall/dto/{ResponseDto.java => ResponseDTO.java} (81%) create mode 100644 src/main/java/shop/mtcoding/metamall/dto/ValidDTO.java create mode 100644 src/main/java/shop/mtcoding/metamall/dto/order/OrderRequest.java delete mode 100644 src/main/java/shop/mtcoding/metamall/dto/product/ProductResponse.java rename src/main/java/shop/mtcoding/metamall/model/{orderproduct => order/product}/OrderProduct.java (80%) rename src/main/java/shop/mtcoding/metamall/model/{orderproduct => order/product}/OrderProductRepository.java (74%) rename src/main/java/shop/mtcoding/metamall/model/{ordersheet => order/sheet}/OrderSheet.java (61%) create mode 100644 src/main/java/shop/mtcoding/metamall/model/order/sheet/OrderSheetRepository.java delete mode 100644 src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java create mode 100644 src/main/java/shop/mtcoding/metamall/util/JwtUtil.java diff --git a/build.gradle b/build.gradle index 4943187..05c9628 100644 --- a/build.gradle +++ b/build.gradle @@ -19,6 +19,8 @@ repositories { } dependencies { + implementation 'org.springframework.boot:spring-boot-starter-aop' + implementation 'org.springframework.boot:spring-boot-starter-validation' implementation group: 'com.auth0', name: 'java-jwt', version: '4.3.0' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' diff --git a/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java b/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java index f5ea4db..ec67b0e 100644 --- a/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java +++ b/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java @@ -5,6 +5,9 @@ import org.springframework.context.annotation.Configuration; import shop.mtcoding.metamall.core.filter.JwtVerifyFilter; +/** + * 인증처리 구간 + */ @Configuration public class FilterRegisterConfig { @@ -12,7 +15,11 @@ public class FilterRegisterConfig { public FilterRegistrationBean jwtVerifyFilterAdd() { FilterRegistrationBean registration = new FilterRegistrationBean<>(); registration.setFilter(new JwtVerifyFilter()); - registration.addUrlPatterns("/user/*"); + registration.addUrlPatterns("/users/*"); + registration.addUrlPatterns("/products/*"); + registration.addUrlPatterns("/orders/*"); + registration.addUrlPatterns("/admin/*"); + registration.addUrlPatterns("/seller/*"); registration.setOrder(1); return registration; } diff --git a/src/main/java/shop/mtcoding/metamall/config/MyWebMvcConfig.java b/src/main/java/shop/mtcoding/metamall/config/MyWebMvcConfig.java new file mode 100644 index 0000000..3d142e1 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/config/MyWebMvcConfig.java @@ -0,0 +1,49 @@ +package shop.mtcoding.metamall.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import shop.mtcoding.metamall.core.interceptor.MyAdminInterceptor; +import shop.mtcoding.metamall.core.interceptor.MySellerInterceptor; +import shop.mtcoding.metamall.core.resolver.MySessionArgumentResolver; + +import java.util.List; + +@RequiredArgsConstructor +@Configuration +public class MyWebMvcConfig implements WebMvcConfigurer { + + private final MyAdminInterceptor adminInterceptor; + private final MySellerInterceptor sellerInterceptor; + private final MySessionArgumentResolver mySessionArgumentResolver; + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedHeaders("*") + .allowedMethods("*") // GET, POST, PUT, DELETE (Javascript 요청 허용) + .allowedOriginPatterns("*") // 모든 IP 주소 허용 (프론트 앤드 IP만 허용하게 변경해야함. * 안됨) + .allowCredentials(true) + .exposedHeaders("Authorization"); // 옛날에는 디폴트로 브라우저에 노출되어 있었는데 지금은 아님 + } + + + // AOP는 매개변수 값 확인해서 권한 비교해야할 때 사용 + // Interceptor는 세션 권한으로 체크할 때 사용 + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(adminInterceptor) + .addPathPatterns("/admin/**"); + + registry.addInterceptor(sellerInterceptor) + .addPathPatterns("/seller/**"); + } + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(mySessionArgumentResolver); + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/config/WebMvcConfig.java b/src/main/java/shop/mtcoding/metamall/config/WebMvcConfig.java deleted file mode 100644 index 64f5d9b..0000000 --- a/src/main/java/shop/mtcoding/metamall/config/WebMvcConfig.java +++ /dev/null @@ -1,18 +0,0 @@ -package shop.mtcoding.metamall.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -public class WebMvcConfig implements WebMvcConfigurer { - @Override - public void addCorsMappings(CorsRegistry registry) { - registry.addMapping("/**") - .allowedHeaders("*") - .allowedMethods("*") // GET, POST, PUT, DELETE (Javascript 요청 허용) - .allowedOriginPatterns("*") // 모든 IP 주소 허용 (프론트 앤드 IP만 허용하게 변경해야함. * 안됨) - .allowCredentials(true) - .exposedHeaders("Authorization"); // 옛날에는 디폴트로 브라우저에 노출되어 있었는데 지금은 아님 - } -} diff --git a/src/main/java/shop/mtcoding/metamall/controller/AdminController.java b/src/main/java/shop/mtcoding/metamall/controller/AdminController.java new file mode 100644 index 0000000..357cb39 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/controller/AdminController.java @@ -0,0 +1,35 @@ +package shop.mtcoding.metamall.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.BindingResult; +import org.springframework.validation.Errors; +import org.springframework.web.bind.annotation.*; +import shop.mtcoding.metamall.core.exception.Exception400; +import shop.mtcoding.metamall.dto.ResponseDTO; +import shop.mtcoding.metamall.dto.user.UserRequest; +import shop.mtcoding.metamall.model.user.User; +import shop.mtcoding.metamall.model.user.UserRepository; + +import javax.validation.Valid; + +/** + * 권한 변경 + */ +@RequiredArgsConstructor +@RestController +public class AdminController { + private final UserRepository userRepository; + + @Transactional // 트랜잭션이 시작되지 않으면 강제로 em.flush() 를 할 수 없고, 더티체킹도 할 수 없다. (원래는 서비스에서) + @PutMapping("/admin/user/{id}/role") + public ResponseEntity updateRole(@PathVariable Long id, @RequestBody @Valid UserRequest.RoleUpdateDTO roleUpdateDTO, Errors errors) { + User userPS = userRepository.findById(id) + .orElseThrow(()-> new Exception400("id", "해당 유저를 찾을 수 없습니다")); + userPS.updateRole(roleUpdateDTO.getRole()); + + ResponseDTO responseDto = new ResponseDTO<>(); + return ResponseEntity.ok().body(responseDto); + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/controller/OrderController.java b/src/main/java/shop/mtcoding/metamall/controller/OrderController.java new file mode 100644 index 0000000..e2afb1a --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/controller/OrderController.java @@ -0,0 +1,144 @@ +package shop.mtcoding.metamall.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.Errors; +import org.springframework.web.bind.annotation.*; +import shop.mtcoding.metamall.core.annotation.MySessionStore; +import shop.mtcoding.metamall.core.exception.Exception400; +import shop.mtcoding.metamall.core.exception.Exception403; +import shop.mtcoding.metamall.core.session.SessionUser; +import shop.mtcoding.metamall.dto.ResponseDTO; +import shop.mtcoding.metamall.dto.order.OrderRequest; +import shop.mtcoding.metamall.model.order.product.OrderProduct; +import shop.mtcoding.metamall.model.order.product.OrderProductRepository; +import shop.mtcoding.metamall.model.order.sheet.OrderSheet; +import shop.mtcoding.metamall.model.order.sheet.OrderSheetRepository; +import shop.mtcoding.metamall.model.product.Product; +import shop.mtcoding.metamall.model.product.ProductRepository; +import shop.mtcoding.metamall.model.user.User; +import shop.mtcoding.metamall.model.user.UserRepository; + +import javax.validation.Valid; +import java.util.List; + +/** + * 주문하기(고객), 주문목록보기(고객), 주문목록보기(판매자), 주문취소하기(고객), 주문취소하기(판매자) + */ +@RequiredArgsConstructor +@RestController +public class OrderController { + private final OrderProductRepository orderProductRepository; + private final OrderSheetRepository orderSheetRepository; + private final ProductRepository productRepository; + private final UserRepository userRepository; + + @Transactional + @PostMapping("/orders") + public ResponseEntity save(@RequestBody @Valid OrderRequest.SaveDTO saveDTO, Errors errors, @MySessionStore SessionUser sessionUser) { + // 1. 세션값으로 유저 찾기 + User userPS = userRepository.findById(sessionUser.getId()) + .orElseThrow( + () -> new Exception400("id", "해당 유저를 찾을 수 없습니다") + ); + + // 2. 상품 찾기 + List productListPS = + productRepository.findAllById(saveDTO.getIds()); + + // 3. 주문 상품 + List orderProductListPS = saveDTO.toEntity(productListPS); + + // 4. 주문서 만들기 + Integer totalPrice = orderProductListPS.stream().mapToInt((orderProduct)-> orderProduct.getOrderPrice()).sum(); + OrderSheet orderSheet = OrderSheet.builder() + .user(userPS) + .totalPrice(totalPrice) + .build(); + OrderSheet orderSheetPS = orderSheetRepository.save(orderSheet); + + // 5. 주문서에 상품추가하고 재고감소하기 + orderProductListPS.stream().forEach( + (orderProductPS -> { + orderSheetPS.addOrderProduct(orderProductPS); + orderProductPS.getProduct().updateQty(orderProductPS.getCount()); + }) + ); + + // 6. 응답하기 + ResponseDTO responseDto = new ResponseDTO<>().data(orderSheetPS); + return ResponseEntity.ok().body(responseDto); + } + + // 유저 주문서 조회 + @GetMapping("/orders") + public ResponseEntity findByUserId(@MySessionStore SessionUser sessionUser){ + List orderSheetListPS = orderSheetRepository.findByUserId(sessionUser.getId()); + ResponseDTO responseDto = new ResponseDTO<>().data(orderSheetListPS); + return ResponseEntity.ok().body(responseDto); + } + + // 그림 설명 필요!! + // 배달의 민족은 하나의 판매자에게서만 주문을 할 수 있다. (다른 판매자의 상품이 담기면, 하나만 담을 수 있게 로직이 변한다) + // 쇼핑몰은 여러 판매자에게서 주문을 할 수 있다. + + // 판매자 주문서 조회 + @GetMapping("/seller/orders") + public ResponseEntity findBySellerId(){ + // 판매자는 한명이기 때문에 orderProductRepository.findAll() 해도 된다. + List orderSheetListPS = orderSheetRepository.findAll(); + ResponseDTO responseDto = new ResponseDTO<>().data(orderSheetListPS); + return ResponseEntity.ok().body(responseDto); + } + + // 유저 주문 취소 + @DeleteMapping("/orders/{id}") + public ResponseEntity delete(@PathVariable Long id, @MySessionStore SessionUser sessionUser){ + // 1. 주문서 찾기 + OrderSheet orderSheetPS = orderSheetRepository.findById(id).orElseThrow( + ()-> new Exception400("id", "해당 주문을 찾을 수 없습니다") + ); + + // 2. 해당 주문서의 주인 여부 확인 + if(!orderSheetPS.getUser().getId().equals(sessionUser.getId())){ + throw new Exception403("권한이 없습니다"); + } + + // 3. 재고 변경하기 + orderSheetPS.getOrderProductList().stream().forEach(orderProduct -> { + orderProduct.getProduct().rollbackQty(orderProduct.getCount()); + }); + + // 4. 주문서 삭제하기 (casecade 옵션) + orderSheetRepository.delete(orderSheetPS); + + // 5. 응답하기 + ResponseDTO responseDto = new ResponseDTO<>(); + return ResponseEntity.ok().body(responseDto); + } + + // 판매자 주문 취소 + @DeleteMapping("/seller/orders/{id}") + public ResponseEntity deleteSeller(@PathVariable Long id){ + // 1. 주문서 찾기 + OrderSheet orderSheetPS = orderSheetRepository.findById(id).orElseThrow( + ()-> new Exception400("id", "해당 주문을 찾을 수 없습니다") + ); + + // 2. 재고 변경하기 + orderSheetPS.getOrderProductList().stream().forEach(orderProduct -> { + orderProduct.getProduct().rollbackQty(orderProduct.getCount()); + }); + + // 3. 주문서 삭제하기 (casecade 옵션) + orderSheetRepository.delete(orderSheetPS); + + // 4. 응답하기 + ResponseDTO responseDto = new ResponseDTO<>(); + return ResponseEntity.ok().body(responseDto); + } + //성공 200통일 + //실패 400,401,403,404,500 + //GET, POST +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/controller/ProductController.java b/src/main/java/shop/mtcoding/metamall/controller/ProductController.java index 7517b9a..f6a57f0 100644 --- a/src/main/java/shop/mtcoding/metamall/controller/ProductController.java +++ b/src/main/java/shop/mtcoding/metamall/controller/ProductController.java @@ -1,91 +1,97 @@ package shop.mtcoding.metamall.controller; import lombok.RequiredArgsConstructor; -import org.apache.coyote.Response; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.Errors; import org.springframework.web.bind.annotation.*; +import shop.mtcoding.metamall.core.annotation.MySessionStore; import shop.mtcoding.metamall.core.exception.Exception400; -import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.core.session.SessionUser; +import shop.mtcoding.metamall.dto.ResponseDTO; import shop.mtcoding.metamall.dto.product.ProductRequest; import shop.mtcoding.metamall.model.product.Product; import shop.mtcoding.metamall.model.product.ProductRepository; +import shop.mtcoding.metamall.model.user.User; import shop.mtcoding.metamall.model.user.UserRepository; -import javax.servlet.http.HttpServletRequest; -import javax.transaction.Transactional; -import java.util.List; -import java.util.Optional; +import javax.servlet.http.HttpSession; +import javax.validation.Valid; +/** + * 상품등록, 상품목록보기, 상품상세보기, 상품수정하기, 상품삭제하기 + */ @RequiredArgsConstructor @RestController public class ProductController { - private final ProductRepository productRepository; private final UserRepository userRepository; + private final HttpSession session; - @PostMapping("/products") - public ResponseEntity productsSave(@RequestBody ProductRequest.ProductSaveDto productDto, HttpServletRequest request ){ - Optional productOP = productRepository.findByName(productDto.getName()); - if(!productOP.isPresent()){ - Product product = Product.builder() - .name(productDto.getName()) - .price(productDto.getPrice()) - .qty(productDto.getQty()) - .build(); - productRepository.save(product); - ResponseDto responseDto = new ResponseDto<>().data(product); - return ResponseEntity.ok().body(responseDto); - }else{ - throw new Exception400 ("중복된 상품명이 있습니다."); - } - } + /** + * + 인증-> 판매자 권한-> 유효성 검사 통과-> 판매자가 존재여부 확인-> 상품등록->응답 + */ - @GetMapping("/products") - public ResponseEntity productList(){ - List productList=productRepository.findAll(); - ResponseDto responseDto=new ResponseDto<>().data(productList); + //인터셉터가 판매자인지 권한체크를 할거니까 + @PostMapping("/seller/products") + public ResponseEntity save(@RequestBody @Valid ProductRequest.SaveDTO saveDTO, Errors errors, @MySessionStore SessionUser sessionUser){ + // 1. 판매자 찾기 + User sellerPS = userRepository.findById(sessionUser.getId()) + .orElseThrow( + ()-> new Exception400("id", "판매자를 찾을 수 없습니다") + ); + + + // 2. 상품 등록하기 + Product productPS = productRepository.save(saveDTO.toEntity(sellerPS)); + ResponseDTO responseDto = new ResponseDTO<>().data(productPS); return ResponseEntity.ok().body(responseDto); } - @GetMapping("/products/{name}") - public ResponseEntity productDetail(@PathVariable String name){ - Optional product=productRepository.findByName(name); - if(product.isPresent()){ - ResponseDto responseDto = new ResponseDto<>().data(product); - return ResponseEntity.ok().body(responseDto); - }else{ - throw new Exception400 ("존재하지 않는 상품입니다. 상품명을 다시 입력하세요 "); - } - } + @GetMapping("/products") + public ResponseEntity findAll(@PageableDefault(size = 10, page = 0, direction = Sort.Direction.DESC) Pageable pageable){ + // 1. 상품 찾기 + Page productPagePS = productRepository.findAll(pageable); - /*** - * - * @param productDto - * @return 상품수정 - */ - @PutMapping("/products/{name}") - public ResponseEntity productUpdate(@RequestBody ProductRequest.ProductUpdateDto productDto){ - Optionalproduct=productRepository.findByName(productDto.getName()); - if(product.isPresent()){ - product.get().updateProduct(productDto); - productRepository.save(product.get()); - ResponseDto responseDto = new ResponseDto<>().data(product); - return ResponseEntity.ok().body(responseDto); - }else{ - throw new Exception400 ("존재하지 않는 상품입니다. 상품명을 다시 입력하세요 "); - } + // 2. 응답하기 + ResponseDTO responseDto = new ResponseDTO<>().data(productPagePS); + return ResponseEntity.ok().body(responseDto); } - @Transactional - @DeleteMapping("/products/{name}") - public ResponseEntity productDelete(@PathVariable String name){ - productRepository.deleteByName(name); - ResponseDtoresponseDto=new ResponseDto<>().data("삭제완료"); - return ResponseEntity.ok().body(responseDto); + @GetMapping("/products/{id}") + public ResponseEntity findById(@PathVariable Long id){ + // 1. 상품 찾기 + Product productPS = productRepository.findById(id).orElseThrow(()-> new Exception400("id", "해당 상품을 찾을 수 없습니다")); + // 2. 응답하기 + ResponseDTO responseDto = new ResponseDTO<>().data(productPS); + return ResponseEntity.ok().body(responseDto); } + @Transactional // 더티체킹 하고 싶다면 붙이기!! + @PutMapping("/seller/products/{id}") + public ResponseEntity update(@PathVariable Long id, @RequestBody @Valid ProductRequest.UpdateDTO updateDTO, Errors errors){ + // 1. 상품 찾기 + Product productPS = productRepository.findById(id).orElseThrow(()-> new Exception400("id", "해당 상품을 찾을 수 없습니다")); + // 2. Update 더티체킹 + productPS.update(updateDTO.getName(), updateDTO.getPrice(), updateDTO.getQty()); + // 3. 응답하기 + ResponseDTO responseDto = new ResponseDTO<>().data(productPS); + return ResponseEntity.ok().body(responseDto); + } -} + @DeleteMapping("/seller/products/{id}") + public ResponseEntity deleteById(@PathVariable Long id){ + Product productPS = productRepository.findById(id).orElseThrow(()-> new Exception400("id", "해당 상품을 찾을 수 없습니다")); + productRepository.delete(productPS); + ResponseDTO responseDto = new ResponseDTO<>(); + return ResponseEntity.ok().body(responseDto); + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/controller/UserController.java b/src/main/java/shop/mtcoding/metamall/controller/UserController.java index a4322f4..6fee52e 100644 --- a/src/main/java/shop/mtcoding/metamall/controller/UserController.java +++ b/src/main/java/shop/mtcoding/metamall/controller/UserController.java @@ -2,83 +2,77 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.validation.Errors; import org.springframework.web.bind.annotation.*; +import shop.mtcoding.metamall.core.annotation.MySameUserIdCheck; import shop.mtcoding.metamall.core.exception.Exception400; -import shop.mtcoding.metamall.core.exception.Exception401; import shop.mtcoding.metamall.core.jwt.JwtProvider; -import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.dto.ResponseDTO; import shop.mtcoding.metamall.dto.user.UserRequest; -import shop.mtcoding.metamall.dto.user.UserResponse; import shop.mtcoding.metamall.model.log.login.LoginLog; import shop.mtcoding.metamall.model.log.login.LoginLogRepository; import shop.mtcoding.metamall.model.user.User; import shop.mtcoding.metamall.model.user.UserRepository; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; +import javax.validation.Valid; import java.time.LocalDateTime; -import java.util.Optional; +/** + * 회원가입, 로그인, 유저상세보기 + */ @RequiredArgsConstructor @RestController public class UserController { private final UserRepository userRepository; private final LoginLogRepository loginLogRepository; - private final HttpSession session; - @PostMapping("/login") - public ResponseEntity login(@RequestBody UserRequest.LoginDto loginDto, HttpServletRequest request) { - Optional userOP = userRepository.findByUsername(loginDto.getUsername()); - if (userOP.isPresent()) { - // 1. 유저 정보 꺼내기 - User loginUser = userOP.get(); - - // 2. 패스워드 검증하기 - if(!loginUser.getPassword().equals(loginDto.getPassword())){ - throw new Exception401("인증되지 않았습니다"); - } - - // 3. JWT 생성하기 - String jwt = JwtProvider.create(userOP.get()); - - // 4. 최종 로그인 날짜 기록 (더티체킹 - update 쿼리 발생) - loginUser.setUpdatedAt(LocalDateTime.now()); - - // 5. 로그 테이블 기록 - LoginLog loginLog = LoginLog.builder() - .userId(loginUser.getId()) - .userAgent(request.getHeader("User-Agent")) - .clientIP(request.getRemoteAddr()) - .build(); - loginLogRepository.save(loginLog); - - // 6. 응답 DTO 생성 - ResponseDto responseDto = new ResponseDto<>().data(loginUser); - return ResponseEntity.ok().header(JwtProvider.HEADER, jwt).body(responseDto); - } else { - throw new Exception400("유저네임 혹은 아이디가 잘못되었습니다"); - } + @PostMapping("/join") + public ResponseEntity join(@RequestBody @Valid UserRequest.JoinDTO joinDTO, Errors errors) { + User userPS = userRepository.save(joinDTO.toEntity()); + ResponseDTO responseDto = new ResponseDTO<>().data(userPS); + return ResponseEntity.ok().body(responseDto); } - /** - * 회원가입 : joinDTO를 받아 Username이 존재하지 않을경우 회원가입 처리 - * */ - @PostMapping("/join") - public ResponseEntity join (@RequestBody UserRequest.JoinDto joinDto, HttpServletRequest request) { - Optional userOP = userRepository.findByUsername(joinDto.getUsername()); // - if(!userOP.isPresent()){ - User user = User.builder().username(joinDto.getUsername()).password(joinDto.getPassword()).email(joinDto.getEmail()).role(joinDto.getRole()).build(); - userRepository.save(user); - ResponseDto responseDto = new ResponseDto<>().data(user); - return ResponseEntity.ok().body(responseDto); - }else { - throw new Exception400("중복된 유저네임 아이디입니다."); + @PostMapping("/login") + public ResponseEntity login(@RequestBody @Valid UserRequest.LoginDTO loginDTO, Errors errors, HttpServletRequest request) { + User userPS = userRepository.findByUsername(loginDTO.getUsername()) + .orElseThrow( + () -> new Exception400("username", "유저네임을 찾을 수 없습니다") + ); + + // 1. 패스워드 검증하기 + if (!userPS.getPassword().equals(loginDTO.getPassword())) { + throw new Exception400("password", "패스워드가 잘못입력되었습니다"); } + // 2. JWT 생성하기 + String jwt = JwtProvider.create(userPS); - } + // 3. 최종 로그인 날짜 기록 (더티체킹 - update 쿼리 발생) + userPS.setUpdatedAt(LocalDateTime.now()); + // 4. 로그 테이블 기록 + LoginLog loginLog = LoginLog.builder() + .userId(userPS.getId()) + .userAgent(request.getHeader("User-Agent")) + .clientIP(request.getRemoteAddr()) + .build(); + loginLogRepository.save(loginLog); + // 5. 응답 DTO 생성 + ResponseDTO responseDto = new ResponseDTO<>().data(userPS); + return ResponseEntity.ok().header(JwtProvider.HEADER, jwt).body(responseDto); + } -} + @MySameUserIdCheck + @GetMapping("/users/{id}") + public ResponseEntity findById(@PathVariable Long id){ + User userPS =userRepository.findById(id).orElseThrow( + ()-> new Exception400("id", "유저를 찾을 수 없습니다") + ); + ResponseDTO responseDto = new ResponseDTO<>().data(userPS); + return ResponseEntity.ok().body(responseDto); + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/advice/AuthorizationAspect.java b/src/main/java/shop/mtcoding/metamall/core/advice/AuthorizationAspect.java deleted file mode 100644 index 002a13a..0000000 --- a/src/main/java/shop/mtcoding/metamall/core/advice/AuthorizationAspect.java +++ /dev/null @@ -1,18 +0,0 @@ -package shop.mtcoding.metamall.core.advice; - -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; -import org.springframework.stereotype.Component; - -@Aspect -@Component -public class AuthorizationAspect { - - @Before("@annotation(shop.mtcoding.metamall.annotation.PermissionCheck)") - public void checkAuthorization(JoinPoint joinPoint) { - // 권한 체크 로직 구현 - // 로그인한 사용자의 권한 정보와 메소드에 설정된 권한 정보를 비교하여 인가 여부 판단 - // 인가되지 않은 경우 예외 처리 등 필요한 작업 수행 - } -} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/advice/MyErrorLogAdvice.java b/src/main/java/shop/mtcoding/metamall/core/advice/MyErrorLogAdvice.java new file mode 100644 index 0000000..1987744 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/advice/MyErrorLogAdvice.java @@ -0,0 +1,42 @@ +package shop.mtcoding.metamall.core.advice; + +import lombok.RequiredArgsConstructor; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.stereotype.Component; +import shop.mtcoding.metamall.core.session.SessionUser; +import shop.mtcoding.metamall.model.log.error.ErrorLog; +import shop.mtcoding.metamall.model.log.error.ErrorLogRepository; + +import javax.servlet.http.HttpSession; + +@RequiredArgsConstructor +@Aspect +@Component +public class MyErrorLogAdvice { //@MyErrorLog라는 어노테이션이 붙어있으면 세션을 체크해 세션이 null아니면 에러로그를 체크해라. + + private final HttpSession session; + private final ErrorLogRepository errorLogRepository; + + @Pointcut("@annotation(shop.mtcoding.metamall.core.annotation.MyErrorLogRecord)") + public void myErrorLog(){} + + @Before("myErrorLog()") + public void errorLogAdvice(JoinPoint jp) throws HttpMessageNotReadableException { + Object[] args = jp.getArgs(); + + for (Object arg : args) { + if(arg instanceof Exception){ + Exception e = (Exception) arg; + SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser"); + if(sessionUser != null){ + ErrorLog errorLog =ErrorLog.builder().userId(sessionUser.getId()).msg(e.getMessage()).build(); + errorLogRepository.save(errorLog); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/advice/MyExceptionAdvice.java b/src/main/java/shop/mtcoding/metamall/core/advice/MyExceptionAdvice.java index 50ebee2..d270d60 100644 --- a/src/main/java/shop/mtcoding/metamall/core/advice/MyExceptionAdvice.java +++ b/src/main/java/shop/mtcoding/metamall/core/advice/MyExceptionAdvice.java @@ -2,41 +2,58 @@ import lombok.RequiredArgsConstructor; 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 shop.mtcoding.metamall.core.exception.*; -import shop.mtcoding.metamall.model.log.error.ErrorLogRepository; +import org.springframework.web.servlet.NoHandlerFoundException; +import shop.mtcoding.metamall.core.annotation.MyErrorLogRecord; +import shop.mtcoding.metamall.core.exception.Exception400; +import shop.mtcoding.metamall.core.exception.Exception401; +import shop.mtcoding.metamall.core.exception.Exception403; +import shop.mtcoding.metamall.dto.ResponseDTO; @Slf4j @RequiredArgsConstructor @RestControllerAdvice public class MyExceptionAdvice { - private final ErrorLogRepository errorLogRepository; + @MyErrorLogRecord @ExceptionHandler(Exception400.class) public ResponseEntity badRequest(Exception400 e){ + log.debug("디버그: ",e.getMessage()); + log.info("인포: ", e.getMessage()); + log.warn("경고: ",e.getMessage()); + log.error("에러: ",e.getMessage()); return new ResponseEntity<>(e.body(), e.status()); } + @MyErrorLogRecord @ExceptionHandler(Exception401.class) public ResponseEntity unAuthorized(Exception401 e){ return new ResponseEntity<>(e.body(), e.status()); } + @MyErrorLogRecord @ExceptionHandler(Exception403.class) public ResponseEntity forbidden(Exception403 e){ - return new ResponseEntity<>(e.body(), e.status()); - } - @ExceptionHandler(Exception404.class) - public ResponseEntity notFound(Exception404 e){ return new ResponseEntity<>(e.body(), e.status()); } + @MyErrorLogRecord + @ExceptionHandler(NoHandlerFoundException.class) + public ResponseEntity notFound(NoHandlerFoundException e){ + ResponseDTO responseDto = new ResponseDTO<>(); + responseDto.fail(HttpStatus.NOT_FOUND, "NotFound", e.getMessage()); + return new ResponseEntity<>(responseDto, HttpStatus.NOT_FOUND); + } - @ExceptionHandler(Exception500.class) - public ResponseEntity serverError(Exception500 e){ - return new ResponseEntity<>(e.body(), e.status()); + @MyErrorLogRecord + @ExceptionHandler(Exception.class) + public ResponseEntity serverError(Exception e){ + ResponseDTO responseDto = new ResponseDTO<>(); + responseDto.fail(HttpStatus.BAD_REQUEST, "BadRequest", e.getMessage()); + return new ResponseEntity<>(responseDto, HttpStatus.NOT_FOUND); } } diff --git a/src/main/java/shop/mtcoding/metamall/core/advice/MySameUserIdAdvice.java b/src/main/java/shop/mtcoding/metamall/core/advice/MySameUserIdAdvice.java new file mode 100644 index 0000000..0015b44 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/advice/MySameUserIdAdvice.java @@ -0,0 +1,48 @@ +package shop.mtcoding.metamall.core.advice; + +import lombok.RequiredArgsConstructor; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; +import shop.mtcoding.metamall.core.exception.Exception403; +import shop.mtcoding.metamall.core.session.SessionUser; + +import javax.servlet.http.HttpSession; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.stream.IntStream; + +@RequiredArgsConstructor +@Aspect +@Component +public class MySameUserIdAdvice { + + private final HttpSession session; + + // 깃발에 별칭주기 + @Pointcut("@annotation(shop.mtcoding.metamall.core.annotation.MySameUserIdCheck)") + public void mySameUserId(){} + + @Before("mySameUserId()") + public void sameUserIdAdvice(JoinPoint jp) { + Object[] args = jp.getArgs(); + MethodSignature signature = (MethodSignature) jp.getSignature(); + Method method = signature.getMethod(); + Parameter[] parameters = method.getParameters(); + + IntStream.range(0, parameters.length).forEach( + (i) -> { + if(parameters[i].getName().equals("id") && parameters[i].getType() == Long.class){ + SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser"); + Long id = (Long) args[i]; + if(sessionUser.getId() != id){ + throw new Exception403("해당 id에 접근할 권한이 없습니다"); + } + } + } + ); + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/advice/MyValidAdvice.java b/src/main/java/shop/mtcoding/metamall/core/advice/MyValidAdvice.java new file mode 100644 index 0000000..bbdbf01 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/advice/MyValidAdvice.java @@ -0,0 +1,38 @@ +package shop.mtcoding.metamall.core.advice; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.stereotype.Component; +import org.springframework.validation.Errors; +import shop.mtcoding.metamall.core.exception.Exception400; + +@Aspect +@Component +public class MyValidAdvice { + @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)") + public void postMapping() { + } + + @Pointcut("@annotation(org.springframework.web.bind.annotation.PutMapping)") + public void putMapping() { + } + + @Before("postMapping() || putMapping()") + public void validationAdvice(JoinPoint jp) { + Object[] args = jp.getArgs(); + for (Object arg : args) { + if (arg instanceof Errors) { + Errors errors = (Errors) arg; + + if (errors.hasErrors()) { + throw new Exception400( + errors.getFieldErrors().get(0).getField(), + errors.getFieldErrors().get(0).getDefaultMessage() + ); + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/annotation/MyErrorLogRecord.java b/src/main/java/shop/mtcoding/metamall/core/annotation/MyErrorLogRecord.java new file mode 100644 index 0000000..9b44cb7 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/annotation/MyErrorLogRecord.java @@ -0,0 +1,11 @@ +package shop.mtcoding.metamall.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface MyErrorLogRecord { +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/annotation/MySameUserIdCheck.java b/src/main/java/shop/mtcoding/metamall/core/annotation/MySameUserIdCheck.java new file mode 100644 index 0000000..61316a8 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/annotation/MySameUserIdCheck.java @@ -0,0 +1,11 @@ +package shop.mtcoding.metamall.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface MySameUserIdCheck { +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/annotation/MySessionStore.java b/src/main/java/shop/mtcoding/metamall/core/annotation/MySessionStore.java new file mode 100644 index 0000000..5bd7606 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/annotation/MySessionStore.java @@ -0,0 +1,11 @@ +package shop.mtcoding.metamall.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface MySessionStore { +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/exception/Exception400.java b/src/main/java/shop/mtcoding/metamall/core/exception/Exception400.java index d1b5fec..07e9155 100644 --- a/src/main/java/shop/mtcoding/metamall/core/exception/Exception400.java +++ b/src/main/java/shop/mtcoding/metamall/core/exception/Exception400.java @@ -2,19 +2,26 @@ import lombok.Getter; import org.springframework.http.HttpStatus; -import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.dto.ResponseDTO; +import shop.mtcoding.metamall.dto.ValidDTO; -// 유효성 실패 +// 유효성 실패(바디데이터), 잘못된 파라미터 요청(뭐가 잘못되었는지) @Getter public class Exception400 extends RuntimeException { - public Exception400(String message) { - super(message); + + private String key; + private String value; + public Exception400(String key,String value) { + super(value); + this.key=key; + this.value=value; } - public ResponseDto body(){ - ResponseDto responseDto = new ResponseDto<>(); - responseDto.fail(HttpStatus.BAD_REQUEST, "badRequest", getMessage()); + public ResponseDTO body(){ + ResponseDTO responseDto = new ResponseDTO<>(); + ValidDTO validDTO=new ValidDTO(key,value); + responseDto.fail(HttpStatus.BAD_REQUEST, "badRequest", validDTO); return responseDto; } diff --git a/src/main/java/shop/mtcoding/metamall/core/exception/Exception401.java b/src/main/java/shop/mtcoding/metamall/core/exception/Exception401.java index 5d2f310..8dcb131 100644 --- a/src/main/java/shop/mtcoding/metamall/core/exception/Exception401.java +++ b/src/main/java/shop/mtcoding/metamall/core/exception/Exception401.java @@ -3,7 +3,7 @@ import lombok.Getter; import org.springframework.http.HttpStatus; -import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.dto.ResponseDTO; // 인증 안됨 @@ -13,8 +13,8 @@ public Exception401(String message) { super(message); } - public ResponseDto body(){ - ResponseDto responseDto = new ResponseDto<>(); + public ResponseDTO body(){ + ResponseDTO responseDto = new ResponseDTO<>(); responseDto.fail(HttpStatus.UNAUTHORIZED, "unAuthorized", getMessage()); return responseDto; } diff --git a/src/main/java/shop/mtcoding/metamall/core/exception/Exception403.java b/src/main/java/shop/mtcoding/metamall/core/exception/Exception403.java index c8dc137..e65c2fc 100644 --- a/src/main/java/shop/mtcoding/metamall/core/exception/Exception403.java +++ b/src/main/java/shop/mtcoding/metamall/core/exception/Exception403.java @@ -2,7 +2,7 @@ import lombok.Getter; import org.springframework.http.HttpStatus; -import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.dto.ResponseDTO; // 권한 없음 @@ -12,8 +12,8 @@ public Exception403(String message) { super(message); } - public ResponseDto body(){ - ResponseDto responseDto = new ResponseDto<>(); + public ResponseDTO body(){ + ResponseDTO responseDto = new ResponseDTO<>(); responseDto.fail(HttpStatus.FORBIDDEN, "forbidden", getMessage()); return responseDto; } diff --git a/src/main/java/shop/mtcoding/metamall/core/exception/Exception404.java b/src/main/java/shop/mtcoding/metamall/core/exception/Exception404.java deleted file mode 100644 index c20b64f..0000000 --- a/src/main/java/shop/mtcoding/metamall/core/exception/Exception404.java +++ /dev/null @@ -1,24 +0,0 @@ -package shop.mtcoding.metamall.core.exception; - -import lombok.Getter; -import org.springframework.http.HttpStatus; -import shop.mtcoding.metamall.dto.ResponseDto; - - -// 리소스 없음 -@Getter -public class Exception404 extends RuntimeException { - public Exception404(String message) { - super(message); - } - - public ResponseDto body(){ - ResponseDto responseDto = new ResponseDto<>(); - responseDto.fail(HttpStatus.NOT_FOUND, "notFound", getMessage()); - return responseDto; - } - - public HttpStatus status(){ - return HttpStatus.NOT_FOUND; - } -} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/exception/Exception500.java b/src/main/java/shop/mtcoding/metamall/core/exception/Exception500.java deleted file mode 100644 index d3d4468..0000000 --- a/src/main/java/shop/mtcoding/metamall/core/exception/Exception500.java +++ /dev/null @@ -1,24 +0,0 @@ -package shop.mtcoding.metamall.core.exception; - -import lombok.Getter; -import org.springframework.http.HttpStatus; -import shop.mtcoding.metamall.dto.ResponseDto; - - -// 서버 에러 -@Getter -public class Exception500 extends RuntimeException { - public Exception500(String message) { - super(message); - } - - public ResponseDto body(){ - ResponseDto responseDto = new ResponseDto<>(); - responseDto.fail(HttpStatus.INTERNAL_SERVER_ERROR, "serverError", getMessage()); - return responseDto; - } - - public HttpStatus status(){ - return HttpStatus.INTERNAL_SERVER_ERROR; - } -} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java b/src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java index 870bf93..621bb1e 100644 --- a/src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java +++ b/src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java @@ -8,8 +8,9 @@ import org.springframework.http.HttpStatus; import shop.mtcoding.metamall.core.exception.Exception400; import shop.mtcoding.metamall.core.jwt.JwtProvider; -import shop.mtcoding.metamall.core.session.LoginUser; -import shop.mtcoding.metamall.dto.ResponseDto; +import shop.mtcoding.metamall.core.session.SessionUser; +import shop.mtcoding.metamall.core.util.MyFilterResponseUtil; +import shop.mtcoding.metamall.dto.ResponseDTO; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; @@ -25,19 +26,19 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha HttpServletResponse resp = (HttpServletResponse) response; String prefixJwt = req.getHeader(JwtProvider.HEADER); if(prefixJwt == null){ - error(resp, new Exception400("토큰이 전달되지 않았습니다")); + MyFilterResponseUtil.badRequest(resp,new Exception400("authorization","토큰이 전달되지 않았습니다. ")); return; } String jwt = prefixJwt.replace(JwtProvider.TOKEN_PREFIX, ""); try { DecodedJWT decodedJWT = JwtProvider.verify(jwt); - int id = decodedJWT.getClaim("id").asInt(); + Long id = decodedJWT.getClaim("id").asLong(); String role = decodedJWT.getClaim("role").asString(); // 세션을 사용하는 이유는 권한처리를 하기 위해서이다. HttpSession session = req.getSession(); - LoginUser loginUser = LoginUser.builder().id(id).role(role).build(); - session.setAttribute("loginUser", loginUser); + SessionUser sessionUser = SessionUser.builder().id(id).role(role).build(); + session.setAttribute("loginUser", sessionUser); chain.doFilter(req, resp); }catch (SignatureVerificationException sve){ error(resp, sve); @@ -49,7 +50,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha private void error(HttpServletResponse resp, Exception e) throws IOException { resp.setStatus(401); resp.setContentType("application/json; charset=utf-8"); - ResponseDto responseDto = new ResponseDto<>().fail(HttpStatus.UNAUTHORIZED, "인증 안됨", e.getMessage()); + ResponseDTO responseDto = new ResponseDTO<>().fail(HttpStatus.UNAUTHORIZED, "인증 안됨", e.getMessage()); ObjectMapper om = new ObjectMapper(); String responseBody = om.writeValueAsString(responseDto); resp.getWriter().println(responseBody); diff --git a/src/main/java/shop/mtcoding/metamall/core/interceptor/MyAdminInterceptor.java b/src/main/java/shop/mtcoding/metamall/core/interceptor/MyAdminInterceptor.java new file mode 100644 index 0000000..0cd24da --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/interceptor/MyAdminInterceptor.java @@ -0,0 +1,24 @@ +package shop.mtcoding.metamall.core.interceptor; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.HandlerInterceptor; +import shop.mtcoding.metamall.core.exception.Exception403; +import shop.mtcoding.metamall.core.session.SessionUser; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +@Configuration +public class MyAdminInterceptor implements HandlerInterceptor { + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + HttpSession session = request.getSession(); + SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser"); + + if(!sessionUser.getRole().equals("ADMIN")){ + throw new Exception403("권한이 없습니다"); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/interceptor/MySellerInterceptor.java b/src/main/java/shop/mtcoding/metamall/core/interceptor/MySellerInterceptor.java new file mode 100644 index 0000000..4f8eedd --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/interceptor/MySellerInterceptor.java @@ -0,0 +1,26 @@ +package shop.mtcoding.metamall.core.interceptor; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.HandlerInterceptor; +import shop.mtcoding.metamall.core.exception.Exception403; +import shop.mtcoding.metamall.core.session.SessionUser; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +@Configuration +public class MySellerInterceptor implements HandlerInterceptor { + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + HttpSession session = request.getSession(); + SessionUser sessionUser = (SessionUser) session.getAttribute("sessionUser"); + + if(sessionUser.getRole().equals("SELLER") || sessionUser.getRole().equals("ADMIN")){ + return true; + }else{ + throw new Exception403("권한이 없습니다"); + } + + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/resolver/MySessionArgumentResolver.java b/src/main/java/shop/mtcoding/metamall/core/resolver/MySessionArgumentResolver.java new file mode 100644 index 0000000..dd05b4f --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/resolver/MySessionArgumentResolver.java @@ -0,0 +1,32 @@ +package shop.mtcoding.metamall.core.resolver; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.MethodParameter; +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; +import shop.mtcoding.metamall.core.annotation.MySessionStore; +import shop.mtcoding.metamall.core.session.SessionUser; + +import javax.servlet.http.HttpSession; + +@RequiredArgsConstructor +@Configuration +public class MySessionArgumentResolver implements HandlerMethodArgumentResolver { + + private final HttpSession session; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + boolean check1 = parameter.getParameterAnnotation(MySessionStore.class) != null; + boolean check2 = SessionUser.class.equals(parameter.getParameterType()); + return check1 && check2; + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + return session.getAttribute("sessionUser"); + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/session/LoginUser.java b/src/main/java/shop/mtcoding/metamall/core/session/SessionUser.java similarity index 66% rename from src/main/java/shop/mtcoding/metamall/core/session/LoginUser.java rename to src/main/java/shop/mtcoding/metamall/core/session/SessionUser.java index 59f402c..20b138b 100644 --- a/src/main/java/shop/mtcoding/metamall/core/session/LoginUser.java +++ b/src/main/java/shop/mtcoding/metamall/core/session/SessionUser.java @@ -4,12 +4,12 @@ import lombok.Getter; @Getter -public class LoginUser { - private Integer id; +public class SessionUser { + private Long id; private String role; @Builder - public LoginUser(Integer id, String role) { + public SessionUser(Long id, String role) { this.id = id; this.role = role; } diff --git a/src/main/java/shop/mtcoding/metamall/core/util/MyDateUtil.java b/src/main/java/shop/mtcoding/metamall/core/util/MyDateUtil.java new file mode 100644 index 0000000..3f92cf6 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/util/MyDateUtil.java @@ -0,0 +1,10 @@ +package shop.mtcoding.metamall.core.util; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class MyDateUtil { + public static String toStringFormat(LocalDateTime localDateTime) { + return localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + } +} diff --git a/src/main/java/shop/mtcoding/metamall/core/util/MyFilterResponseUtil.java b/src/main/java/shop/mtcoding/metamall/core/util/MyFilterResponseUtil.java new file mode 100644 index 0000000..b4ed1eb --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/core/util/MyFilterResponseUtil.java @@ -0,0 +1,40 @@ +package shop.mtcoding.metamall.core.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.http.HttpStatus; +import shop.mtcoding.metamall.core.exception.Exception400; +import shop.mtcoding.metamall.dto.ResponseDTO; +import shop.mtcoding.metamall.dto.ValidDTO; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +//Filter는 예외핸들러로 처리 불가능 +public class MyFilterResponseUtil { + public static void badRequest(HttpServletResponse resp, Exception e) throws IOException { + resp.setStatus(400); + resp.setContentType("application/json; charset=utf-8"); + ResponseDTO responseDto = new ResponseDTO<>().fail(HttpStatus.BAD_REQUEST, "badRequest", e.getMessage()); + ObjectMapper om = new ObjectMapper(); + String responseBody = om.writeValueAsString(responseDto); + resp.getWriter().println(responseBody); + } + public static void unAuthorized(HttpServletResponse resp, Exception400 e) throws IOException { + resp.setStatus(401); + resp.setContentType("application/json; charset=utf-8"); + ValidDTO validDTO=new ValidDTO(e.getKey(),e.getValue()); + ResponseDTO responseDto = new ResponseDTO<>().fail(HttpStatus.UNAUTHORIZED, "unAuthorized", validDTO); + ObjectMapper om = new ObjectMapper(); + String responseBody = om.writeValueAsString(responseDto); + resp.getWriter().println(responseBody); + } + + public static void forbidden(HttpServletResponse resp, Exception e) throws IOException { + resp.setStatus(403); + resp.setContentType("application/json; charset=utf-8"); + ResponseDTO responseDto = new ResponseDTO<>().fail(HttpStatus.FORBIDDEN, "forbidden", e.getMessage()); + ObjectMapper om = new ObjectMapper(); + String responseBody = om.writeValueAsString(responseDto); + resp.getWriter().println(responseBody); + } +} diff --git a/src/main/java/shop/mtcoding/metamall/dto/ResponseDto.java b/src/main/java/shop/mtcoding/metamall/dto/ResponseDTO.java similarity index 81% rename from src/main/java/shop/mtcoding/metamall/dto/ResponseDto.java rename to src/main/java/shop/mtcoding/metamall/dto/ResponseDTO.java index 7f190c6..fd8c682 100644 --- a/src/main/java/shop/mtcoding/metamall/dto/ResponseDto.java +++ b/src/main/java/shop/mtcoding/metamall/dto/ResponseDTO.java @@ -4,23 +4,23 @@ import org.springframework.http.HttpStatus; @Getter -public class ResponseDto { +public class ResponseDTO { private Integer status; // 에러시에 의미 있음. private String msg; // 에러시에 의미 있음. ex) badRequest private T data; // 에러시에는 구체적인 에러 내용 ex) username이 입력되지 않았습니다 - public ResponseDto(){ + public ResponseDTO(){ this.status = HttpStatus.OK.value(); this.msg = "성공"; this.data = null; } - public ResponseDto data(T data){ + public ResponseDTO data(T data){ this.data = data; // 응답할 데이터 바디 return this; } - public ResponseDto fail(HttpStatus httpStatus, String msg, T data){ + public ResponseDTO fail(HttpStatus httpStatus, String msg, T data){ this.status = httpStatus.value(); this.msg = msg; // 에러 제목 this.data = data; // 에러 내용 diff --git a/src/main/java/shop/mtcoding/metamall/dto/ValidDTO.java b/src/main/java/shop/mtcoding/metamall/dto/ValidDTO.java new file mode 100644 index 0000000..8ae6813 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/dto/ValidDTO.java @@ -0,0 +1,13 @@ +package shop.mtcoding.metamall.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +public class ValidDTO { + private String key; + private String value; +} diff --git a/src/main/java/shop/mtcoding/metamall/dto/order/OrderRequest.java b/src/main/java/shop/mtcoding/metamall/dto/order/OrderRequest.java new file mode 100644 index 0000000..1472b35 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/dto/order/OrderRequest.java @@ -0,0 +1,46 @@ +package shop.mtcoding.metamall.dto.order; + +import lombok.Getter; +import lombok.Setter; +import shop.mtcoding.metamall.model.order.product.OrderProduct; +import shop.mtcoding.metamall.model.product.Product; + +import java.util.List; +import java.util.stream.Collectors; + +public class OrderRequest { + @Getter + @Setter + public static class SaveDTO { + + private List orderProducts; + + @Getter + @Setter + public static class OrderProductDTO { + private Long productId; + private Integer count; + } + + public List getIds() { + return orderProducts.stream().map((orderProduct) -> orderProduct.getProductId()).collect(Collectors.toList()); + } + + public List toEntity(List products) { + + return orderProducts.stream() + .flatMap((orderProduct) -> { + Long productId = orderProduct.productId; + Integer count = orderProduct.getCount(); + return products.stream() + .filter((product) -> product.getId().equals(productId)) + .map((product) -> OrderProduct.builder() + .product(product) + .count(count) + .orderPrice(product.getPrice() * count) + .build()); + } + ).collect(Collectors.toList()); + } + } +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/dto/product/ProductRequest.java b/src/main/java/shop/mtcoding/metamall/dto/product/ProductRequest.java index e315e81..5977737 100644 --- a/src/main/java/shop/mtcoding/metamall/dto/product/ProductRequest.java +++ b/src/main/java/shop/mtcoding/metamall/dto/product/ProductRequest.java @@ -2,22 +2,51 @@ import lombok.Getter; import lombok.Setter; +import shop.mtcoding.metamall.model.product.Product; +import shop.mtcoding.metamall.model.user.User; -import java.time.LocalDateTime; +import javax.validation.constraints.Digits; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; public class ProductRequest { - @Getter - @Setter - public static class ProductSaveDto{ + + @Getter @Setter + public static class SaveDTO { + @NotEmpty private String name; + + // 숫자는 NotNull로 검증한다 + @Digits(fraction = 0, integer = 9) + @NotNull private Integer price; + + @Digits(fraction = 0, integer = 9) + @NotNull private Integer qty; + + + public Product toEntity(User seller){ + return Product.builder() + .name(name) + .price(price) + .qty(qty) + .seller(seller) + .build(); + } } - @Getter - @Setter - public static class ProductUpdateDto{ + + @Getter @Setter + public static class UpdateDTO { + @NotEmpty private String name; + + @Digits(fraction = 0, integer = 9) + @NotNull private Integer price; + + @Digits(fraction = 0, integer = 9) + @NotNull private Integer qty; } -} +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/dto/product/ProductResponse.java b/src/main/java/shop/mtcoding/metamall/dto/product/ProductResponse.java deleted file mode 100644 index 11d3e67..0000000 --- a/src/main/java/shop/mtcoding/metamall/dto/product/ProductResponse.java +++ /dev/null @@ -1,4 +0,0 @@ -package shop.mtcoding.metamall.dto.product; - -public class ProductResponse { -} diff --git a/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java b/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java index c82d916..2561c1c 100644 --- a/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java +++ b/src/main/java/shop/mtcoding/metamall/dto/user/UserRequest.java @@ -4,22 +4,56 @@ import lombok.Setter; import shop.mtcoding.metamall.model.user.User; -import java.time.LocalDateTime; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; public class UserRequest { + @Getter @Setter - public static class LoginDto { + public static class JoinDTO { + + // Size는 String에만 쓸 수 있다. + @NotEmpty + @Size(min = 3, max = 20) private String username; + + @NotEmpty + @Size(min = 4, max = 20) // DB에는 60자, 실제 받을 때는 20자 이하 private String password; + + // Pattern은 String에만 쓸 수 있다. + @NotEmpty + @Pattern(regexp = "^[\\w._%+-]+@[\\w.-]+\\.[a-zA-Z]{2,6}$", message = "이메일 형식이 아닙니다") + private String email; + + @NotEmpty + @Pattern(regexp = "USER|SELLER|ADMIN") + private String role; + + public User toEntity(){ + return User.builder() + .username(username) + .password(password) + .email(email) + .role(role) + .status(true) + .build(); + } } - @Getter - @Setter - public static class JoinDto{ + + @Getter @Setter + public static class LoginDTO { + @NotEmpty private String username; + @NotEmpty private String password; - private String email; - private String role; // USER(고객), SELLER(판매자), ADMIN(관리자) - + } + @Getter @Setter + public static class RoleUpdateDTO { + @NotEmpty + private String role; } -} + +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/model/log/error/ErrorLog.java b/src/main/java/shop/mtcoding/metamall/model/log/error/ErrorLog.java index a7279e7..fbfe7e5 100644 --- a/src/main/java/shop/mtcoding/metamall/model/log/error/ErrorLog.java +++ b/src/main/java/shop/mtcoding/metamall/model/log/error/ErrorLog.java @@ -14,7 +14,6 @@ @Table(name = "error_log_tb") @Entity public class ErrorLog { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; diff --git a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java b/src/main/java/shop/mtcoding/metamall/model/order/product/OrderProduct.java similarity index 80% rename from src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java rename to src/main/java/shop/mtcoding/metamall/model/order/product/OrderProduct.java index 9209aa5..07b401e 100644 --- a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProduct.java +++ b/src/main/java/shop/mtcoding/metamall/model/order/product/OrderProduct.java @@ -1,10 +1,10 @@ -package shop.mtcoding.metamall.model.orderproduct; +package shop.mtcoding.metamall.model.order.product; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import shop.mtcoding.metamall.model.ordersheet.OrderSheet; +import shop.mtcoding.metamall.model.order.sheet.OrderSheet; import shop.mtcoding.metamall.model.product.Product; import javax.persistence.*; @@ -19,15 +19,19 @@ public class OrderProduct { // 주문 상품 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + //checkpoint -> 무한참조 @ManyToOne private Product product; + @Column(nullable = false) private Integer count; // 상품 주문 개수 + @Column(nullable = false) private Integer orderPrice; // 상품 주문 금액 + private LocalDateTime createdAt; private LocalDateTime updatedAt; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name="sheet_id") + @ManyToOne private OrderSheet orderSheet; @PrePersist @@ -40,6 +44,10 @@ protected void onUpdate() { this.updatedAt = LocalDateTime.now(); } + public void syncOrderSheet(OrderSheet orderSheet){ + this.orderSheet=orderSheet; + } + @Builder public OrderProduct(Long id, Product product, Integer count, Integer orderPrice, LocalDateTime createdAt, LocalDateTime updatedAt, OrderSheet orderSheet) { this.id = id; diff --git a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProductRepository.java b/src/main/java/shop/mtcoding/metamall/model/order/product/OrderProductRepository.java similarity index 74% rename from src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProductRepository.java rename to src/main/java/shop/mtcoding/metamall/model/order/product/OrderProductRepository.java index 6f1238c..1b686dc 100644 --- a/src/main/java/shop/mtcoding/metamall/model/orderproduct/OrderProductRepository.java +++ b/src/main/java/shop/mtcoding/metamall/model/order/product/OrderProductRepository.java @@ -1,4 +1,4 @@ -package shop.mtcoding.metamall.model.orderproduct; +package shop.mtcoding.metamall.model.order.product; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java b/src/main/java/shop/mtcoding/metamall/model/order/sheet/OrderSheet.java similarity index 61% rename from src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java rename to src/main/java/shop/mtcoding/metamall/model/order/sheet/OrderSheet.java index 1827add..994c881 100644 --- a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheet.java +++ b/src/main/java/shop/mtcoding/metamall/model/order/sheet/OrderSheet.java @@ -1,11 +1,11 @@ -package shop.mtcoding.metamall.model.ordersheet; +package shop.mtcoding.metamall.model.order.sheet; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import shop.mtcoding.metamall.model.orderproduct.OrderProduct; -import shop.mtcoding.metamall.model.product.Product; +import shop.mtcoding.metamall.model.order.product.OrderProduct; import shop.mtcoding.metamall.model.user.User; import javax.persistence.*; @@ -22,15 +22,27 @@ public class OrderSheet { // 주문서 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name="user_id") + @ManyToOne private User user; // 주문자 - @OneToMany(cascade = CascadeType.ALL, mappedBy ="orderSheet", fetch = FetchType.LAZY) + //checkpoint ->무한참조 + + @JsonIgnoreProperties({"orderSheet"}) + @OneToMany(mappedBy = "orderSheet", cascade = CascadeType.ALL, orphanRemoval = true) //고아객체->자동삭제 O(주문서가 없어진다면) private List orderProductList = new ArrayList<>(); // 총 주문 상품 리스트 + @Column(nullable = false) private Integer totalPrice; // 총 주문 금액 (총 주문 상품 리스트의 orderPrice 합) private LocalDateTime createdAt; private LocalDateTime updatedAt; + public void addOrderProduct(OrderProduct orderProduct){ + orderProductList.add(orderProduct); + orderProduct.syncOrderSheet(this); //내 주문서(id)를 여기에 넣어줌 + } + public void removeOrderProduct(OrderProduct orderProduct){ + orderProductList.remove(orderProduct); + orderProduct.syncOrderSheet(null); //주문서를 빼줘야함 + } + @PrePersist protected void onCreate() { this.createdAt = LocalDateTime.now(); diff --git a/src/main/java/shop/mtcoding/metamall/model/order/sheet/OrderSheetRepository.java b/src/main/java/shop/mtcoding/metamall/model/order/sheet/OrderSheetRepository.java new file mode 100644 index 0000000..769589f --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/model/order/sheet/OrderSheetRepository.java @@ -0,0 +1,12 @@ +package shop.mtcoding.metamall.model.order.sheet; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface OrderSheetRepository extends JpaRepository { + @Query("select os from OrderSheet os where os.user.id = :userId") + List findByUserId(@Param("userId") Long userId); +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java b/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java deleted file mode 100644 index 5d59249..0000000 --- a/src/main/java/shop/mtcoding/metamall/model/ordersheet/OrderSheetRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package shop.mtcoding.metamall.model.ordersheet; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface OrderSheetRepository extends JpaRepository { -} diff --git a/src/main/java/shop/mtcoding/metamall/model/product/Product.java b/src/main/java/shop/mtcoding/metamall/model/product/Product.java index f76b29d..57098ad 100644 --- a/src/main/java/shop/mtcoding/metamall/model/product/Product.java +++ b/src/main/java/shop/mtcoding/metamall/model/product/Product.java @@ -4,7 +4,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import shop.mtcoding.metamall.dto.product.ProductRequest; +import shop.mtcoding.metamall.model.user.User; import javax.persistence.*; import java.time.LocalDateTime; @@ -18,8 +18,13 @@ public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @ManyToOne + private User seller; + @Column(nullable = false, length = 50) private String name; // 상품 이름 + @Column(nullable = false) private Integer price; // 상품 가격 + @Column(nullable = false) private Integer qty; // 상품 재고 private LocalDateTime createdAt; private LocalDateTime updatedAt; @@ -35,18 +40,38 @@ protected void onUpdate() { } @Builder - public Product(Long id, String name, Integer price, Integer qty, LocalDateTime createdAt, LocalDateTime updatedAt) { + public Product(Long id, User seller, String name, Integer price, Integer qty, LocalDateTime createdAt, LocalDateTime updatedAt) { this.id = id; + this.seller = seller; this.name = name; this.price = price; this.qty = qty; this.createdAt = createdAt; this.updatedAt = updatedAt; } - public void updateProduct(ProductRequest.ProductUpdateDto productUpdateDto){ - this.name= productUpdateDto.getName(); - this.qty=productUpdateDto.getQty(); - this.price=productUpdateDto.getPrice(); + + /** + 상품 변경 (판매자) + */ + public void update(String name, Integer price, Integer qty){ + this.name=name; + this.price=price; + this.qty=qty; } + /** + 주문시 재고 변경(구매자) + */ + public void updateQty(Integer orderCount){ + if(this.qty { - Optional findByName(String name); - void deleteByName(String name); } diff --git a/src/main/java/shop/mtcoding/metamall/model/user/User.java b/src/main/java/shop/mtcoding/metamall/model/user/User.java index 0c172e0..49eed50 100644 --- a/src/main/java/shop/mtcoding/metamall/model/user/User.java +++ b/src/main/java/shop/mtcoding/metamall/model/user/User.java @@ -1,54 +1,71 @@ package shop.mtcoding.metamall.model.user; import com.fasterxml.jackson.annotation.JsonIgnore; -import lombok.*; -import shop.mtcoding.metamall.model.ordersheet.OrderSheet; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import shop.mtcoding.metamall.core.exception.Exception400; import javax.persistence.*; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -@Entity -@Builder -@Getter @NoArgsConstructor -@ToString -@Setter -@AllArgsConstructor -@Table(name="user_tb") +@Setter // DTO 만들면 삭제해야됨 +@Getter +@Table(name = "user_tb") +@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(unique = true, nullable = false, length = 20) private String username; @JsonIgnore + @Column(nullable = false, length = 60) // BCrypt로 암호화 하면 60Byte private String password; + @Column(nullable = false, length = 50) private String email; + @Column(nullable = false, length = 10) private String role; // USER(고객), SELLER(판매자), ADMIN(관리자) + @Column(nullable = false) + private Boolean status; // 활성, 비활성 + @Column(nullable = false) private LocalDateTime createdAt; private LocalDateTime updatedAt; - @OneToMany(cascade = CascadeType.ALL, mappedBy ="user", fetch = FetchType.LAZY) - private List orderSheet = new ArrayList<>(); - @PrePersist //insert직전에 발동 + + public void updateRole(String role){ + if(this.role.equals(role)){ + throw new Exception400("role",""); + } + this.role = role; + } + + public void delete(){ + this.status = false; + } + + @PrePersist protected void onCreate() { this.createdAt = LocalDateTime.now(); } - @PreUpdate //update할때 발동 + @PreUpdate protected void onUpdate() { this.updatedAt = LocalDateTime.now(); } @Builder - public User(Long id, String username, String password, String email, String role, LocalDateTime createdAt) { + public User(Long id, String username, String password, String email, String role, Boolean status, LocalDateTime createdAt, LocalDateTime updatedAt) { this.id = id; this.username = username; this.password = password; this.email = email; this.role = role; + this.status = status; this.createdAt = createdAt; + this.updatedAt = updatedAt; } -} +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/model/user/UserRepository.java b/src/main/java/shop/mtcoding/metamall/model/user/UserRepository.java index 293a101..2f642f9 100644 --- a/src/main/java/shop/mtcoding/metamall/model/user/UserRepository.java +++ b/src/main/java/shop/mtcoding/metamall/model/user/UserRepository.java @@ -6,7 +6,7 @@ import java.util.Optional; -public interface UserRepository extends JpaRepository { +public interface UserRepository extends JpaRepository { @Query("select u from User u where u.username = :username") Optional findByUsername(@Param("username") String username); diff --git a/src/main/java/shop/mtcoding/metamall/util/JwtUtil.java b/src/main/java/shop/mtcoding/metamall/util/JwtUtil.java new file mode 100644 index 0000000..83dc050 --- /dev/null +++ b/src/main/java/shop/mtcoding/metamall/util/JwtUtil.java @@ -0,0 +1,34 @@ +package shop.mtcoding.metamall.util; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import org.springframework.stereotype.Component; + +/** +* JWT에서 권한정보 추출 +* */ +@Component +public class JwtUtil { + + private static final String SECRET = "메타코딩"; + private static final String AUTH_HEADER = "Authorization"; + private static final String TOKEN_PREFIX = "Bearer "; + + public String extractUsername(String token) { + Claims claims = Jwts.parser() + .setSigningKey(SECRET) + .parseClaimsJws(token.replace(TOKEN_PREFIX, "")) + .getBody(); + + return claims.getSubject(); + } + + public String extractRole(String token) { + Claims claims = Jwts.parser() + .setSigningKey(SECRET) + .parseClaimsJws(token.replace(TOKEN_PREFIX, "")) + .getBody(); + + return claims.get("role", String.class); + } +} + diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1d9bd50..b1b36a2 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -15,13 +15,15 @@ spring: enabled: true jpa: hibernate: - ddl-auto: create + ddl-auto: update show-sql: true properties: hibernate: format_sql: true default_batch_fetch_size: 100 # in query 자동 작성 + + logging: level: '[shop.mtcoding.metamall]': DEBUG # DEBUG 레벨부터 에러 확인할 수 있게 설정하기 From 5c39507bb1b495aa164d2fdd287809bb4b0ad8b4 Mon Sep 17 00:00:00 2001 From: yeeeeerim Date: Mon, 17 Apr 2023 15:57:01 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=EC=98=A4=EB=A5=98=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../metamall/MetamallApplication.java | 18 +-- .../shop/mtcoding/metamall/util/JwtUtil.java | 47 ++++---- .../mtcoding/metamall/regex/RegexTest.java | 110 ++++++++++++++++++ 3 files changed, 142 insertions(+), 33 deletions(-) create mode 100644 src/test/java/shop/mtcoding/metamall/regex/RegexTest.java diff --git a/src/main/java/shop/mtcoding/metamall/MetamallApplication.java b/src/main/java/shop/mtcoding/metamall/MetamallApplication.java index 487bb62..5d2baa6 100644 --- a/src/main/java/shop/mtcoding/metamall/MetamallApplication.java +++ b/src/main/java/shop/mtcoding/metamall/MetamallApplication.java @@ -4,24 +4,24 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; -import shop.mtcoding.metamall.model.orderproduct.OrderProduct; -import shop.mtcoding.metamall.model.orderproduct.OrderProductRepository; -import shop.mtcoding.metamall.model.ordersheet.OrderSheet; -import shop.mtcoding.metamall.model.ordersheet.OrderSheetRepository; +import shop.mtcoding.metamall.model.order.product.OrderProductRepository; +import shop.mtcoding.metamall.model.order.sheet.OrderSheetRepository; import shop.mtcoding.metamall.model.product.ProductRepository; import shop.mtcoding.metamall.model.user.User; import shop.mtcoding.metamall.model.user.UserRepository; +import java.util.Arrays; + @SpringBootApplication public class MetamallApplication { @Bean CommandLineRunner initData(UserRepository userRepository, ProductRepository productRepository, OrderProductRepository orderProductRepository, OrderSheetRepository orderSheetRepository){ return (args)->{ - // 여기에서 save 하면 됨. - // bulk Collector는 saveAll 하면 됨. - User ssar = User.builder().username("ssar").password("1234").email("ssar@nate.com").role("USER").build(); - userRepository.save(ssar); + User ssar = User.builder().username("ssar").password("1234").email("ssar@nate.com").role("USER").status(true).build(); + User seller = User.builder().username("seller").password("1234").email("seller@nate.com").role("SELLER").status(true).build(); + User admin = User.builder().username("admin").password("1234").email("admin@nate.com").role("ADMIN").status(true).build(); + userRepository.saveAll(Arrays.asList(ssar, seller, admin)); }; } @@ -29,4 +29,4 @@ public static void main(String[] args) { SpringApplication.run(MetamallApplication.class, args); } -} +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/util/JwtUtil.java b/src/main/java/shop/mtcoding/metamall/util/JwtUtil.java index 83dc050..6d4a3b2 100644 --- a/src/main/java/shop/mtcoding/metamall/util/JwtUtil.java +++ b/src/main/java/shop/mtcoding/metamall/util/JwtUtil.java @@ -1,6 +1,5 @@ package shop.mtcoding.metamall.util; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; + import org.springframework.stereotype.Component; /** @@ -8,27 +7,27 @@ * */ @Component public class JwtUtil { - - private static final String SECRET = "메타코딩"; - private static final String AUTH_HEADER = "Authorization"; - private static final String TOKEN_PREFIX = "Bearer "; - - public String extractUsername(String token) { - Claims claims = Jwts.parser() - .setSigningKey(SECRET) - .parseClaimsJws(token.replace(TOKEN_PREFIX, "")) - .getBody(); - - return claims.getSubject(); - } - - public String extractRole(String token) { - Claims claims = Jwts.parser() - .setSigningKey(SECRET) - .parseClaimsJws(token.replace(TOKEN_PREFIX, "")) - .getBody(); - - return claims.get("role", String.class); - } +// +// private static final String SECRET = "메타코딩"; +// private static final String AUTH_HEADER = "Authorization"; +// private static final String TOKEN_PREFIX = "Bearer "; +// +// public String extractUsername(String token) { +// Claims claims = Jwts.parser() +// .setSigningKey(SECRET) +// .parseClaimsJws(token.replace(TOKEN_PREFIX, "")) +// .getBody(); +// +// return claims.getSubject(); +// } +// +// public String extractRole(String token) { +// Claims claims = Jwts.parser() +// .setSigningKey(SECRET) +// .parseClaimsJws(token.replace(TOKEN_PREFIX, "")) +// .getBody(); +// +// return claims.get("role", String.class); +// } } diff --git a/src/test/java/shop/mtcoding/metamall/regex/RegexTest.java b/src/test/java/shop/mtcoding/metamall/regex/RegexTest.java new file mode 100644 index 0000000..4b7a717 --- /dev/null +++ b/src/test/java/shop/mtcoding/metamall/regex/RegexTest.java @@ -0,0 +1,110 @@ +package shop.mtcoding.metamall.regex; + +import org.junit.jupiter.api.Test; + +import java.util.regex.Pattern; + +/** + * 가장 좋은 방법은 ChatGPT에게 물어보는 것이다. [스프링부트에서 특수문자 제외시키는 정규표현식 알려줘] + */ +public class RegexTest { + @Test + public void 한글만된다_test() throws Exception { + String value = "한글"; + boolean result = Pattern.matches("^[가-힣]+$", value); + System.out.println("테스트 : " + result); + } + + @Test + public void 한글은안된다_test() throws Exception { + String value = "abc"; + boolean result = Pattern.matches("^[^ㄱ-ㅎ가-힣]*$", value); + System.out.println("테스트 : " + result); + } + + @Test + public void 영어만된다_test() throws Exception { + String value = "ssar"; + boolean result = Pattern.matches("^[a-zA-Z]+$", value); + System.out.println("테스트 : " + result); + } + + @Test + public void 영어는안된다_test() throws Exception { + String value = "가22"; + boolean result = Pattern.matches("^[^a-zA-Z]*$", value); + System.out.println("테스트 : " + result); + } + + @Test + public void 영어와숫자만된다_test() throws Exception { + String value = "ab12"; + boolean result = Pattern.matches("^[a-zA-Z0-9]+$", value); + System.out.println("테스트 : " + result); + } + + @Test + public void 영어만되고_길이는최소2최대4이다_test() throws Exception { + String value = "ssar"; + boolean result = Pattern.matches("^[a-zA-Z]{2,4}$", value); + System.out.println("테스트 : " + result); + } + + // username, email, fullname + @Test + public void user_username_test() throws Exception { + String username = "ssar"; + boolean result = Pattern.matches("^[a-zA-Z0-9]{2,20}$", username); + System.out.println("테스트 : " + result); + } + + @Test + public void user_fullname_test() throws Exception { + String fullname = "메타코딩"; + boolean result = Pattern.matches("^[a-zA-Z가-힣]{1,20}$", fullname); + System.out.println("테스트 : " + result); + } + + @Test + public void user_email_test() throws Exception { + String fullname = "ssaraa@nate.com"; // ac.kr co.kr or.kr + boolean result = Pattern.matches("^[a-zA-Z0-9]{2,10}@[a-zA-Z0-9]{2,6}\\.[a-zA-Z]{2,3}$", fullname); + System.out.println("테스트 : " + result); + } + + // ChatGPT에게 물어봄! [스프링부트에서 이메일 정규표현식 알려줘] + @Test + public void real_email_test() throws Exception { + String email = "ssar@ac"; + boolean result = Pattern.matches("^[\\w._%+-]+@[\\w.-]+\\.[a-zA-Z]{2,6}$", email); + System.out.println("테스트 : "+result); + } + + @Test + public void account_gubun_test1() throws Exception { + String gubun = "DEPOSIT"; // ac.kr co.kr or.kr + boolean result = Pattern.matches("^(DEPOSIT)$", gubun); + System.out.println("테스트 : " + result); + } + + @Test + public void account_gubun_test2() throws Exception { + String gubun = "TRANSFER"; // ac.kr co.kr or.kr + boolean result = Pattern.matches("^(DEPOSIT|TRANSFER)$", gubun); + System.out.println("테스트 : " + result); + } + + @Test + public void account_tel_test1() throws Exception { + String tel = "010-3333-7777"; // ac.kr co.kr or.kr + boolean result = Pattern.matches("^[0-9]{3}-[0-9]{4}-[0-9]{4}", tel); + System.out.println("테스트 : " + result); + } + + @Test + public void account_tel_test2() throws Exception { + String tel = "01033337777"; // ac.kr co.kr or.kr + boolean result = Pattern.matches("^[0-9]{11}", tel); + System.out.println("테스트 : " + result); + } +} \ No newline at end of file From 86b42cbcb2b44f2ff4d2bf00fa674ab37cc04a57 Mon Sep 17 00:00:00 2001 From: yeeeeerim Date: Mon, 17 Apr 2023 16:59:43 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=EA=B6=8C=ED=95=9C=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...onfig.java => MyFilterRegisterConfig.java} | 15 ++++----- .../controller/ProductController.java | 2 -- ...rifyFilter.java => MyJwtVerifyFilter.java} | 32 +++++++------------ ...seUtil.java => MyFilterResponseUtils.java} | 20 +++--------- 4 files changed, 22 insertions(+), 47 deletions(-) rename src/main/java/shop/mtcoding/metamall/config/{FilterRegisterConfig.java => MyFilterRegisterConfig.java} (69%) rename src/main/java/shop/mtcoding/metamall/core/filter/{JwtVerifyFilter.java => MyJwtVerifyFilter.java} (57%) rename src/main/java/shop/mtcoding/metamall/core/util/{MyFilterResponseUtil.java => MyFilterResponseUtils.java} (58%) diff --git a/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java b/src/main/java/shop/mtcoding/metamall/config/MyFilterRegisterConfig.java similarity index 69% rename from src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java rename to src/main/java/shop/mtcoding/metamall/config/MyFilterRegisterConfig.java index ec67b0e..88c24ed 100644 --- a/src/main/java/shop/mtcoding/metamall/config/FilterRegisterConfig.java +++ b/src/main/java/shop/mtcoding/metamall/config/MyFilterRegisterConfig.java @@ -3,18 +3,16 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import shop.mtcoding.metamall.core.filter.JwtVerifyFilter; +import shop.mtcoding.metamall.core.filter.MyJwtVerifyFilter; -/** - * 인증처리 구간 - */ @Configuration -public class FilterRegisterConfig { +public class MyFilterRegisterConfig { + @Bean public FilterRegistrationBean jwtVerifyFilterAdd() { - FilterRegistrationBean registration = new FilterRegistrationBean<>(); - registration.setFilter(new JwtVerifyFilter()); + FilterRegistrationBean registration = new FilterRegistrationBean<>(); + registration.setFilter(new MyJwtVerifyFilter()); registration.addUrlPatterns("/users/*"); registration.addUrlPatterns("/products/*"); registration.addUrlPatterns("/orders/*"); @@ -23,4 +21,5 @@ public FilterRegistrationBean jwtVerifyFilterAdd() { registration.setOrder(1); return registration; } -} + +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/controller/ProductController.java b/src/main/java/shop/mtcoding/metamall/controller/ProductController.java index f6a57f0..e52a684 100644 --- a/src/main/java/shop/mtcoding/metamall/controller/ProductController.java +++ b/src/main/java/shop/mtcoding/metamall/controller/ProductController.java @@ -45,8 +45,6 @@ public ResponseEntity save(@RequestBody @Valid ProductRequest.SaveDTO saveDTO .orElseThrow( ()-> new Exception400("id", "판매자를 찾을 수 없습니다") ); - - // 2. 상품 등록하기 Product productPS = productRepository.save(saveDTO.toEntity(sellerPS)); ResponseDTO responseDto = new ResponseDTO<>().data(productPS); diff --git a/src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java b/src/main/java/shop/mtcoding/metamall/core/filter/MyJwtVerifyFilter.java similarity index 57% rename from src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java rename to src/main/java/shop/mtcoding/metamall/core/filter/MyJwtVerifyFilter.java index 621bb1e..d8f239a 100644 --- a/src/main/java/shop/mtcoding/metamall/core/filter/JwtVerifyFilter.java +++ b/src/main/java/shop/mtcoding/metamall/core/filter/MyJwtVerifyFilter.java @@ -4,13 +4,10 @@ import com.auth0.jwt.exceptions.SignatureVerificationException; import com.auth0.jwt.exceptions.TokenExpiredException; import com.auth0.jwt.interfaces.DecodedJWT; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.http.HttpStatus; import shop.mtcoding.metamall.core.exception.Exception400; import shop.mtcoding.metamall.core.jwt.JwtProvider; import shop.mtcoding.metamall.core.session.SessionUser; -import shop.mtcoding.metamall.core.util.MyFilterResponseUtil; -import shop.mtcoding.metamall.dto.ResponseDTO; +import shop.mtcoding.metamall.core.util.MyFilterResponseUtils; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; @@ -18,42 +15,35 @@ import javax.servlet.http.HttpSession; import java.io.IOException; -public class JwtVerifyFilter implements Filter { +public class MyJwtVerifyFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - System.out.println("디버그 : JwtVerifyFilter 동작함"); HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; String prefixJwt = req.getHeader(JwtProvider.HEADER); + if(prefixJwt == null){ - MyFilterResponseUtil.badRequest(resp,new Exception400("authorization","토큰이 전달되지 않았습니다. ")); + MyFilterResponseUtils.unAuthorized(resp, new Exception400("authorization", "토큰이 전달되지 않았습니다")); return; } + String jwt = prefixJwt.replace(JwtProvider.TOKEN_PREFIX, ""); try { DecodedJWT decodedJWT = JwtProvider.verify(jwt); Long id = decodedJWT.getClaim("id").asLong(); String role = decodedJWT.getClaim("role").asString(); - // 세션을 사용하는 이유는 권한처리를 하기 위해서이다. + // 세션을 사용하는 이유는 role(권한) 처리를 하기 위해서이다. HttpSession session = req.getSession(); SessionUser sessionUser = SessionUser.builder().id(id).role(role).build(); - session.setAttribute("loginUser", sessionUser); + session.setAttribute("sessionUser", sessionUser); + System.out.println("세션 생성됨"); chain.doFilter(req, resp); }catch (SignatureVerificationException sve){ - error(resp, sve); + MyFilterResponseUtils.unAuthorized(resp, sve); }catch (TokenExpiredException tee){ - error(resp, tee); + MyFilterResponseUtils.unAuthorized(resp, tee); } } - private void error(HttpServletResponse resp, Exception e) throws IOException { - resp.setStatus(401); - resp.setContentType("application/json; charset=utf-8"); - ResponseDTO responseDto = new ResponseDTO<>().fail(HttpStatus.UNAUTHORIZED, "인증 안됨", e.getMessage()); - ObjectMapper om = new ObjectMapper(); - String responseBody = om.writeValueAsString(responseDto); - resp.getWriter().println(responseBody); - } - -} +} \ No newline at end of file diff --git a/src/main/java/shop/mtcoding/metamall/core/util/MyFilterResponseUtil.java b/src/main/java/shop/mtcoding/metamall/core/util/MyFilterResponseUtils.java similarity index 58% rename from src/main/java/shop/mtcoding/metamall/core/util/MyFilterResponseUtil.java rename to src/main/java/shop/mtcoding/metamall/core/util/MyFilterResponseUtils.java index b4ed1eb..8e116fa 100644 --- a/src/main/java/shop/mtcoding/metamall/core/util/MyFilterResponseUtil.java +++ b/src/main/java/shop/mtcoding/metamall/core/util/MyFilterResponseUtils.java @@ -2,28 +2,16 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.http.HttpStatus; -import shop.mtcoding.metamall.core.exception.Exception400; import shop.mtcoding.metamall.dto.ResponseDTO; -import shop.mtcoding.metamall.dto.ValidDTO; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -//Filter는 예외핸들러로 처리 불가능 -public class MyFilterResponseUtil { - public static void badRequest(HttpServletResponse resp, Exception e) throws IOException { - resp.setStatus(400); - resp.setContentType("application/json; charset=utf-8"); - ResponseDTO responseDto = new ResponseDTO<>().fail(HttpStatus.BAD_REQUEST, "badRequest", e.getMessage()); - ObjectMapper om = new ObjectMapper(); - String responseBody = om.writeValueAsString(responseDto); - resp.getWriter().println(responseBody); - } - public static void unAuthorized(HttpServletResponse resp, Exception400 e) throws IOException { +public class MyFilterResponseUtils { + public static void unAuthorized(HttpServletResponse resp, Exception e) throws IOException { resp.setStatus(401); resp.setContentType("application/json; charset=utf-8"); - ValidDTO validDTO=new ValidDTO(e.getKey(),e.getValue()); - ResponseDTO responseDto = new ResponseDTO<>().fail(HttpStatus.UNAUTHORIZED, "unAuthorized", validDTO); + ResponseDTO responseDto = new ResponseDTO<>().fail(HttpStatus.UNAUTHORIZED, "unAuthorized", e.getMessage()); ObjectMapper om = new ObjectMapper(); String responseBody = om.writeValueAsString(responseDto); resp.getWriter().println(responseBody); @@ -37,4 +25,4 @@ public static void forbidden(HttpServletResponse resp, Exception e) throws IOExc String responseBody = om.writeValueAsString(responseDto); resp.getWriter().println(responseBody); } -} +} \ No newline at end of file