From 396d61faa6563a2bb8414ffb397f8af196af73d9 Mon Sep 17 00:00:00 2001 From: JiHoon Date: Sat, 13 Sep 2025 13:49:14 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat=20:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=EC=85=89=ED=84=B0=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book_bot/common/config/WebConfig.java | 21 +++ .../common/intercept/AuthInterceptor.java | 56 +++++++ .../controller/order/OrderController.java | 2 +- .../controller/order/OrderViewController.java | 3 + .../book_bot/repository/UserRepository.java | 3 + .../book_bot/service/auth/UserService.java | 65 ++++++++ .../book_bot/service/order/OrderService.java | 2 +- src/main/resources/templates/book/detail.html | 141 ++++++++++++++++-- 8 files changed, 275 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/fastcampus/book_bot/common/config/WebConfig.java create mode 100644 src/main/java/com/fastcampus/book_bot/common/intercept/AuthInterceptor.java create mode 100644 src/main/java/com/fastcampus/book_bot/service/auth/UserService.java diff --git a/src/main/java/com/fastcampus/book_bot/common/config/WebConfig.java b/src/main/java/com/fastcampus/book_bot/common/config/WebConfig.java new file mode 100644 index 0000000..6499b6f --- /dev/null +++ b/src/main/java/com/fastcampus/book_bot/common/config/WebConfig.java @@ -0,0 +1,21 @@ +package com.fastcampus.book_bot.common.config; + +import com.fastcampus.book_bot.common.intercept.AuthInterceptor; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +@RequiredArgsConstructor +public class WebConfig implements WebMvcConfigurer { + + private final AuthInterceptor authInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(authInterceptor) + .addPathPatterns("/order/**") + .excludePathPatterns("/login", "/register"); + } +} diff --git a/src/main/java/com/fastcampus/book_bot/common/intercept/AuthInterceptor.java b/src/main/java/com/fastcampus/book_bot/common/intercept/AuthInterceptor.java new file mode 100644 index 0000000..c85ac78 --- /dev/null +++ b/src/main/java/com/fastcampus/book_bot/common/intercept/AuthInterceptor.java @@ -0,0 +1,56 @@ +package com.fastcampus.book_bot.common.intercept; + +import com.fastcampus.book_bot.common.utils.JwtUtil; +import com.fastcampus.book_bot.domain.user.User; +import com.fastcampus.book_bot.service.auth.UserService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import java.io.IOException; + +@Component +@RequiredArgsConstructor +@Slf4j +public class AuthInterceptor implements HandlerInterceptor { + + private final UserService userService; + private final JwtUtil jwtUtil; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + try { + String authorization = request.getHeader("Authorization"); + if (authorization == null || !authorization.startsWith("Bearer ")) { + response.sendRedirect("/login"); + return false; + } + + String accessToken = authorization.substring(7); + + if (jwtUtil.isTokenExpired(accessToken)) { + response.sendRedirect("/login"); + return false; + } + + Integer userId = jwtUtil.extractUserId(accessToken); + + User currentUser = userService.getUserById(userId); + + request.setAttribute("currentUser", currentUser); + + return true; + } catch (Exception e) { + log.error("interceptor: 인증 실패: {}", e.getMessage()); + try { + response.sendRedirect("/login"); + } catch (IOException ioException) { + log.error("리다이렉트 실패", ioException); + } + return false; + } + } +} diff --git a/src/main/java/com/fastcampus/book_bot/controller/order/OrderController.java b/src/main/java/com/fastcampus/book_bot/controller/order/OrderController.java index 4fddbfe..a0b698c 100644 --- a/src/main/java/com/fastcampus/book_bot/controller/order/OrderController.java +++ b/src/main/java/com/fastcampus/book_bot/controller/order/OrderController.java @@ -19,7 +19,7 @@ public class OrderController { @PostMapping("/complete") public ResponseEntity> orderComplete(OrdersDTO ordersDTO) { - orderService.orderBook(ordersDTO); + return ResponseEntity.ok(SuccessApiResponse.of("")); } diff --git a/src/main/java/com/fastcampus/book_bot/controller/order/OrderViewController.java b/src/main/java/com/fastcampus/book_bot/controller/order/OrderViewController.java index d84da5e..6560bcd 100644 --- a/src/main/java/com/fastcampus/book_bot/controller/order/OrderViewController.java +++ b/src/main/java/com/fastcampus/book_bot/controller/order/OrderViewController.java @@ -10,6 +10,9 @@ public class OrderViewController { @PostMapping("/order") public String order(@ModelAttribute("orderForm") OrdersDTO ordersDTO) { + + + return "order/order"; } diff --git a/src/main/java/com/fastcampus/book_bot/repository/UserRepository.java b/src/main/java/com/fastcampus/book_bot/repository/UserRepository.java index d762387..9099c24 100644 --- a/src/main/java/com/fastcampus/book_bot/repository/UserRepository.java +++ b/src/main/java/com/fastcampus/book_bot/repository/UserRepository.java @@ -4,6 +4,7 @@ import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; +import java.util.Optional; public interface UserRepository extends JpaRepository { @@ -11,4 +12,6 @@ public interface UserRepository extends JpaRepository { boolean existsByUserNickname(String userNickname); User findByUserEmail(String userEmail); + + Optional findByUserId(Integer userId); } diff --git a/src/main/java/com/fastcampus/book_bot/service/auth/UserService.java b/src/main/java/com/fastcampus/book_bot/service/auth/UserService.java new file mode 100644 index 0000000..5bee8e7 --- /dev/null +++ b/src/main/java/com/fastcampus/book_bot/service/auth/UserService.java @@ -0,0 +1,65 @@ +package com.fastcampus.book_bot.service.auth; + +import com.fastcampus.book_bot.common.exception.user.UserDomainException; +import com.fastcampus.book_bot.common.exception.user.UserErrorCode; +import com.fastcampus.book_bot.common.utils.JwtUtil; +import com.fastcampus.book_bot.domain.user.User; +import com.fastcampus.book_bot.repository.UserRepository; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Slf4j +public class UserService { + + private final JwtUtil jwtUtil; + private final UserRepository userRepository; + + @Transactional + public User getCurrentUser(HttpServletRequest request) { + try { + // Authorization 헤더에서 토큰 추출 + String authorization = request.getHeader("Authorization"); + if (authorization == null || !authorization.startsWith("Bearer ")) { + throw new UserDomainException(UserErrorCode.UNAUTHORIZED_ACCESS.getMessage(), + UserErrorCode.UNAUTHORIZED_ACCESS.getCode(), HttpStatus.UNAUTHORIZED); + } + + String accessToken = authorization.substring(7); + + // 토큰 유효성 검사 + if (jwtUtil.isTokenExpired(accessToken)) { + throw new UserDomainException(UserErrorCode.UNAUTHORIZED_ACCESS.getMessage(), + UserErrorCode.UNAUTHORIZED_ACCESS.getCode(), HttpStatus.UNAUTHORIZED); + } + + // 토큰에서 사용자 ID 추출 + Integer userId = jwtUtil.extractUserId(accessToken); + + // DB에서 사용자 정보 조회 (등급 정보 포함) + return (User) userRepository.findByUserId(userId) + .orElseThrow(() -> new UserDomainException(UserErrorCode.UNAUTHORIZED_ACCESS.getMessage(), + UserErrorCode.UNAUTHORIZED_ACCESS.getCode(), HttpStatus.UNAUTHORIZED)); + + } catch (Exception e) { + log.error("현재 사용자 정보 조회 실패: {}", e.getMessage()); + throw new UserDomainException(UserErrorCode.UNAUTHORIZED_ACCESS.getMessage(), + UserErrorCode.UNAUTHORIZED_ACCESS.getCode(), HttpStatus.UNAUTHORIZED); + } + } + + /** + * 사용자 ID로 사용자 정보 조회 (내부 서비스용) + */ + @Transactional + public User getUserById(Integer userId) { + return (User) userRepository.findByUserId(userId) + .orElseThrow(() -> new UserDomainException(UserErrorCode.UNAUTHORIZED_ACCESS.getMessage(), + UserErrorCode.UNAUTHORIZED_ACCESS.getCode(), HttpStatus.UNAUTHORIZED)); + } +} diff --git a/src/main/java/com/fastcampus/book_bot/service/order/OrderService.java b/src/main/java/com/fastcampus/book_bot/service/order/OrderService.java index fc0291d..b1a0afc 100644 --- a/src/main/java/com/fastcampus/book_bot/service/order/OrderService.java +++ b/src/main/java/com/fastcampus/book_bot/service/order/OrderService.java @@ -20,7 +20,7 @@ public class OrderService { @Transactional public void orderBook(OrdersDTO ordersDTO) { - /* ordersDTO to order Entity */ + } diff --git a/src/main/resources/templates/book/detail.html b/src/main/resources/templates/book/detail.html index 53c8c38..f1efc6c 100644 --- a/src/main/resources/templates/book/detail.html +++ b/src/main/resources/templates/book/detail.html @@ -99,7 +99,6 @@ min-width: 150px; } - /* 주문 버튼 특별 스타일 */ .btn-order { background: linear-gradient(135deg, #28a745 0%, #20c997 100%); border: none; @@ -117,6 +116,12 @@ color: white; } + .btn-order:disabled { + background: #6c757d; + cursor: not-allowed; + transform: none; + } + .breadcrumb-custom { background-color: transparent; padding: 0; @@ -163,6 +168,14 @@ margin-top: 30px; } + .loading { + display: none; + } + + .loading.show { + display: inline-block; + } + @media (max-width: 768px) { .book-title { font-size: 1.5rem; @@ -258,18 +271,15 @@

도서 제목

- -
- - - - - - - -
+ + 도서 소개
- +