From 6b60e7fd87ba7a04213f69621a60cb5775dd6c17 Mon Sep 17 00:00:00 2001 From: humpose Date: Sat, 9 Nov 2024 05:38:44 +0900 Subject: [PATCH 01/38] =?UTF-8?q?refactor:=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81=ED=95=B4=EC=84=9C=20dto=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EC=97=AD=ED=95=A0=20=ED=99=95=EC=8B=A4=ED=9E=88=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../work/aeatbe/service/ArticleService.java | 123 +++++++----------- 1 file changed, 44 insertions(+), 79 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/service/ArticleService.java b/src/main/java/jeje/work/aeatbe/service/ArticleService.java index 962176d0..c3396e27 100644 --- a/src/main/java/jeje/work/aeatbe/service/ArticleService.java +++ b/src/main/java/jeje/work/aeatbe/service/ArticleService.java @@ -27,15 +27,8 @@ public class ArticleService { private final ArticleRepository articleRepository; - /** - * 새로운 칼럼을 데이터베이스에 저장 - * - * @param articleDTO 생성할 칼럼의 정보가 포함된 DTO - * @return 생성된 칼럼의 DTO - */ public ArticleDTO createArticle(ArticleDTO articleDTO) { Article article = Article.builder() - .id(articleDTO.id()) .title(articleDTO.title()) .date(articleDTO.date()) .author(articleDTO.author()) @@ -44,102 +37,51 @@ public ArticleDTO createArticle(ArticleDTO articleDTO) { .thumbnailUrl(articleDTO.thumbnailUrl()) .likes(articleDTO.likes()) .build(); - articleRepository.save(article); - return articleDTO; + + article = articleRepository.save(article); + return mapToDTO(article); } - /** - * 필터링 및 페이지네이션이 적용된 칼럼 목록 반환 - * - * @param category 칼럼의 카테고리 - * @param title 칼럼의 제목 - * @param subtitle 칼럼의 소제목 - * @param pageable 페이지네이션 정보 - * @return 필터링된 칼럼 목록과 페이지 정보가 포함된 DTO - */ public ArticleListResponseDTO getArticles(String category, String title, String subtitle, Pageable pageable) { - Page
articlePage = applyFilters(category, title, subtitle, pageable); - PageInfoDTO pageInfo = new PageInfoDTO( - (int) articlePage.getTotalElements(), - pageable.getPageSize() - ); + PageInfoDTO pageInfo = PageInfoDTO.builder() + .totalResults((int) articlePage.getTotalElements()) + .resultsPerPage(pageable.getPageSize()) + .build(); List columns = articlePage.getContent().stream() - .map(article -> ArticleResponseDTO.builder() - .id(article.getId()) - .title(article.getTitle()) - .imgurl(article.getThumbnailUrl()) - .createdAt(article.getDate()) - .auth(article.getAuthor()) - .keyword(Arrays.asList(article.getTags().split(","))) - .content(null) - .subtitle(ArticleUtil.extractSubtitle(article.getContent())) - .build()) + .map(this::mapToResponseDTO) .collect(Collectors.toList()); return new ArticleListResponseDTO(columns, pageInfo); } - /** - * 특정 칼럼 반환 - * - * @param id 반환할 칼럼의 ID - * @return 요청된 칼럼의 세부 정보가 포함된 DTO - */ public ArticleResponseDTO getArticleById(Long id) { Article article = findArticle(id); - - List keywords = Arrays.asList(article.getTags().split(",")); - List contentList = ArticleUtil.extractContentList(article.getContent()); - - return ArticleResponseDTO.builder() - .id(article.getId()) - .title(article.getTitle()) - .imgurl(article.getThumbnailUrl()) - .createdAt(article.getDate()) - .auth(article.getAuthor()) - .keyword(keywords) - .content(contentList) - .subtitle(ArticleUtil.extractSubtitle(article.getContent())) - .build(); + return mapToResponseDTO(article); } - /** - * 칼럼 업데이트 - * - * @param id 업데이트할 칼럼의 ID - * @param articleDTO 업데이트할 내용이 담긴 DTO - * @return 업데이트된 칼럼의 DTO - */ public ArticleDTO updateArticle(Long id, ArticleDTO articleDTO) { Article existingArticle = findArticle(id); - Article updatedArticle = Article.builder() + existingArticle = Article.builder() .id(existingArticle.getId()) - .title(articleDTO.title()) + .title(articleDTO.title() != null ? articleDTO.title() : existingArticle.getTitle()) .date(articleDTO.date() != null ? articleDTO.date() : existingArticle.getDate()) - .author(articleDTO.author()) - .tags(articleDTO.tags()) - .content(articleDTO.content()) - .thumbnailUrl(articleDTO.thumbnailUrl()) - .likes(articleDTO.likes()) + .author(articleDTO.author() != null ? articleDTO.author() : existingArticle.getAuthor()) + .tags(articleDTO.tags() != null ? articleDTO.tags() : existingArticle.getTags()) + .content(articleDTO.content() != null ? articleDTO.content() : existingArticle.getContent()) + .thumbnailUrl(articleDTO.thumbnailUrl() != null ? articleDTO.thumbnailUrl() : existingArticle.getThumbnailUrl()) + .likes(existingArticle.getLikes()) .build(); - articleRepository.save(updatedArticle); - - return articleDTO; + articleRepository.save(existingArticle); + return mapToDTO(existingArticle); } - /** - * 칼럼 삭제 - * - * @param id 삭제할 칼럼의 ID - */ public void deleteArticle(Long id) { Article article = findArticle(id); - articleRepository.delete(article); } @@ -154,10 +96,8 @@ private Page
applyFilters(String category, String title, String subtitl return articleRepository.findByTagsContaining(category, pageable); } else if (title != null && !title.isEmpty()) { return articleRepository.findByTitleContaining(title, pageable); - } else if (subtitle != null && !subtitle.isEmpty()) { - return articleRepository.findByContentContaining(subtitle, pageable); } else { - return articleRepository.findAll(pageable); + return articleRepository.findByContentContaining(subtitle, pageable); } } @@ -166,5 +106,30 @@ private Article findArticle(Long id) { .orElseThrow(() -> new ColumnNotFoundException("Article with id " + id + " not found")); } + private ArticleDTO mapToDTO(Article article) { + return ArticleDTO.builder() + .id(article.getId()) + .title(article.getTitle()) + .date(article.getDate()) + .author(article.getAuthor()) + .tags(article.getTags()) + .content(article.getContent()) + .thumbnailUrl(article.getThumbnailUrl()) + .likes(article.getLikes()) + .build(); + } + + private ArticleResponseDTO mapToResponseDTO(Article article) { + return ArticleResponseDTO.builder() + .id(article.getId()) + .title(article.getTitle()) + .imgurl(article.getThumbnailUrl()) + .createdAt(article.getDate()) + .auth(article.getAuthor()) + .keyword(Arrays.asList(article.getTags().split(","))) + .content(ArticleUtil.extractContentList(article.getContent())) + .subtitle(ArticleUtil.extractSubtitle(article.getContent())) + .build(); + } } From b32af02a7819edb0d1ff9d6b138543accf6add32 Mon Sep 17 00:00:00 2001 From: humpose Date: Sat, 9 Nov 2024 05:39:32 +0900 Subject: [PATCH 02/38] =?UTF-8?q?test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jeje/work/aeatbe/WishListServiceTest.java | 362 +++++++++--------- 1 file changed, 180 insertions(+), 182 deletions(-) diff --git a/src/test/java/jeje/work/aeatbe/WishListServiceTest.java b/src/test/java/jeje/work/aeatbe/WishListServiceTest.java index d9238166..779ed4a6 100644 --- a/src/test/java/jeje/work/aeatbe/WishListServiceTest.java +++ b/src/test/java/jeje/work/aeatbe/WishListServiceTest.java @@ -1,182 +1,180 @@ -//package jeje.work.aeatbe; -// -//import jeje.work.aeatbe.dto.product.ProductDTO; -//import jeje.work.aeatbe.dto.wishlist.WishDTO; -//import jeje.work.aeatbe.dto.wishlist.WishProductDTO; -//import jeje.work.aeatbe.entity.Product; -//import jeje.work.aeatbe.entity.User; -//import jeje.work.aeatbe.entity.Wishlist; -//import jeje.work.aeatbe.mapper.product.ProductMapper; -//import jeje.work.aeatbe.repository.UserRepository; -//import jeje.work.aeatbe.repository.WishlistRepository; -//import jeje.work.aeatbe.service.ProductService; -//import jeje.work.aeatbe.service.WishListService; -//import org.junit.jupiter.api.BeforeEach; -//import org.junit.jupiter.api.DisplayName; -//import org.junit.jupiter.api.Test; -//import org.junit.jupiter.api.extension.ExtendWith; -//import org.mockito.InjectMocks; -//import org.mockito.Mock; -//import org.mockito.junit.jupiter.MockitoExtension; -// -//import java.util.List; -//import java.util.Optional; -// -//import static org.junit.jupiter.api.Assertions.assertEquals; -//import static org.junit.jupiter.api.Assertions.assertNotNull; -//import static org.mockito.Mockito.*; -//@ExtendWith(MockitoExtension.class) -//class WishListServiceTest { -// -// @Mock -// private WishlistRepository wishlistRepository; -// -// @Mock -// private ProductService productService; -// -// @Mock -// private ProductMapper productMapper; -// -// @Mock -// private UserRepository userRepository; -// -// @InjectMocks -// private WishListService wishListService; -// -// private User user; -// private Product product; -// private Wishlist wishlist; -// private WishDTO wishDTO; -// private ProductDTO productDTO; -// -// @BeforeEach -// void setUp() { -// user = User.builder() -// .kakaoId("kakao_12345") -// .allergies("Nuts, Dairy") -// .freeFrom("Gluten") -// .userName("John Doe") -// .userImgUrl("http://example.com/user.jpg") -// .accessToken("access_token") -// .refreshToken("refresh_token") -// .build(); -// user.setId(1L); -// -// product = Product.builder() -// .id(1L) -// .productName("Test Product") -// .price(100L) -// .productImageUrl("http://example.com/product.jpg") -// .typeName("Electronics") -// .build(); -// -// wishlist = new Wishlist(1L, user, product); -// -// productDTO = ProductDTO.builder() -// .id(1L) -// .productName("Test Product") -// .price(100L) -// .productImageUrl("http://example.com/product.jpg") -// .build(); -// -// wishDTO = WishDTO.builder() -// .id(1L) -// .product(WishProductDTO.builder() -// .id(product.getId()) -// .name(product.getProductName()) -// .price(product.getPrice()) -// .imgurl(product.getProductImageUrl()) -// .tag(product.getTypeName()) -// .build()) -// .build(); -// } -// -// @Test -// @DisplayName("위시리스트 생성") -// void createWish_Success() { -// // given -// Long userId = user.getId(); -// Long productId = product.getId(); -// -// when(userRepository.findById(userId)).thenReturn(Optional.of(user)); -// when(productService.getProductDTO(productId)).thenReturn(productDTO); -// when(productMapper.toEntity(productDTO, true)).thenReturn(product); -// when(wishlistRepository.save(any(Wishlist.class))).thenReturn(wishlist); -// -// // when -// WishDTO result = wishListService.createWish(userId, productId); -// -// // then -// assertNotNull(result); -// verify(userRepository).findById(userId); -// verify(productService).getProductDTO(productId); -// verify(wishlistRepository).save(any(Wishlist.class)); -// } -// -// @Test -// @DisplayName("위시리스트 조회") -// void getWishlist_Success() { -// // given -// Long userId = user.getId(); -// -// when(userRepository.findById(userId)).thenReturn(Optional.of(user)); -// when(wishlistRepository.findByUserId(userId)).thenReturn(List.of(wishlist)); -// -// // when -// List result = wishListService.getWishlist(userId); -// -// // then -// assertNotNull(result); -// assertEquals(1, result.size()); -// assertEquals(wishlist.getId(), result.get(0).id()); -// verify(userRepository).findById(userId); -// verify(wishlistRepository).findByUserId(userId); -// } -// -// @Test -// @DisplayName("위시리스트 업데이트") -// void updateWish_Success() { -// // given -// Long userId = user.getId(); -// Long wishId = wishlist.getId(); -// Long newProductId = 2L; -// -// Product newProduct = Product.builder() -// .id(newProductId) -// .productName("Updated Product") -// .price(150L) -// .productImageUrl("http://example.com/product_updated.jpg") -// .build(); -// -// when(userRepository.findById(userId)).thenReturn(Optional.of(user)); -// when(wishlistRepository.findByIdAndUserId(wishId, userId)).thenReturn(Optional.of(wishlist)); -// when(productService.getProductDTO(newProductId)).thenReturn(productDTO); -// when(productMapper.toEntity(productDTO, true)).thenReturn(newProduct); -// -// // when -// wishListService.updateWish(userId, wishId, newProductId); -// -// // then -// verify(wishlistRepository).findByIdAndUserId(wishId, userId); -// verify(productService).getProductDTO(newProductId); -// verify(wishlistRepository).save(any(Wishlist.class)); -// } -// -// @Test -// @DisplayName("위시리스트 삭제") -// void deleteWish_Success() { -// // given -// Long userId = user.getId(); -// Long wishId = wishlist.getId(); -// -// when(userRepository.findById(userId)).thenReturn(Optional.of(user)); -// when(wishlistRepository.findByIdAndUserId(wishId, userId)).thenReturn(Optional.of(wishlist)); -// -// // when -// wishListService.deleteWish(userId, wishId); -// -// // then -// verify(wishlistRepository).findByIdAndUserId(wishId, userId); -// verify(wishlistRepository).delete(wishlist); -// } -//} \ No newline at end of file +package jeje.work.aeatbe; + +import jeje.work.aeatbe.dto.product.ProductDTO; +import jeje.work.aeatbe.dto.wishlist.WishDTO; +import jeje.work.aeatbe.dto.wishlist.WishProductDTO; +import jeje.work.aeatbe.entity.Product; +import jeje.work.aeatbe.entity.User; +import jeje.work.aeatbe.entity.Wishlist; +import jeje.work.aeatbe.mapper.product.ProductMapper; +import jeje.work.aeatbe.repository.UserRepository; +import jeje.work.aeatbe.repository.WishlistRepository; +import jeje.work.aeatbe.service.ProductService; +import jeje.work.aeatbe.service.WishListService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.*; +@ExtendWith(MockitoExtension.class) +class WishListServiceTest { + + @Mock + private WishlistRepository wishlistRepository; + + @Mock + private ProductService productService; + + @Mock + private ProductMapper productMapper; + + @Mock + private UserRepository userRepository; + + @InjectMocks + private WishListService wishListService; + + private User user; + private Product product; + private Wishlist wishlist; + private WishDTO wishDTO; + private ProductDTO productDTO; + + @BeforeEach + void setUp() { + user = User.builder() + .kakaoId("kakao_12345") + .userName("John Doe") + .userImgUrl("http://example.com/user.jpg") + .kakaoAccessToken("access_token") + .kakaoRefreshToken("refresh_token") + .jwtRefreshToken("jwtRefreshToken") + .build(); + + product = Product.builder() + .id(1L) + .productName("Test Product") + .price(100L) + .productImageUrl("http://example.com/product.jpg") + .typeName("Electronics") + .build(); + + wishlist = new Wishlist(1L, user, product); + + productDTO = ProductDTO.builder() + .id(1L) + .productName("Test Product") + .price(100L) + .productImageUrl("http://example.com/product.jpg") + .build(); + + wishDTO = WishDTO.builder() + .id(1L) + .product(WishProductDTO.builder() + .id(product.getId()) + .name(product.getProductName()) + .price(product.getPrice()) + .imgurl(product.getProductImageUrl()) + .tag(product.getTypeName()) + .build()) + .build(); + } + + @Test + @DisplayName("위시리스트 생성") + void createWish_Success() { + // given + Long userId = user.getId(); + Long productId = product.getId(); + + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + when(productService.getProductDTO(productId)).thenReturn(productDTO); + when(productMapper.toEntity(productDTO, true)).thenReturn(product); + when(wishlistRepository.save(any(Wishlist.class))).thenReturn(wishlist); + + // when + WishDTO result = wishListService.createWish(userId, productId); + + // then + assertNotNull(result); + verify(userRepository).findById(userId); + verify(productService).getProductDTO(productId); + verify(wishlistRepository).save(any(Wishlist.class)); + } + + @Test + @DisplayName("위시리스트 조회") + void getWishlist_Success() { + // given + Long userId = user.getId(); + + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + when(wishlistRepository.findByUserId(userId)).thenReturn(List.of(wishlist)); + + // when + List result = wishListService.getWishlist(userId); + + // then + assertNotNull(result); + assertEquals(1, result.size()); + assertEquals(wishlist.getId(), result.get(0).id()); + verify(userRepository).findById(userId); + verify(wishlistRepository).findByUserId(userId); + } + + @Test + @DisplayName("위시리스트 업데이트") + void updateWish_Success() { + // given + Long userId = user.getId(); + Long wishId = wishlist.getId(); + Long newProductId = 2L; + + Product newProduct = Product.builder() + .id(newProductId) + .productName("Updated Product") + .price(150L) + .productImageUrl("http://example.com/product_updated.jpg") + .build(); + + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + when(wishlistRepository.findByIdAndUserId(wishId, userId)).thenReturn(Optional.of(wishlist)); + when(productService.getProductDTO(newProductId)).thenReturn(productDTO); + when(productMapper.toEntity(productDTO, true)).thenReturn(newProduct); + + // when + wishListService.updateWish(userId, wishId, newProductId); + + // then + verify(wishlistRepository).findByIdAndUserId(wishId, userId); + verify(productService).getProductDTO(newProductId); + verify(wishlistRepository).save(any(Wishlist.class)); + } + + @Test + @DisplayName("위시리스트 삭제") + void deleteWish_Success() { + // given + Long userId = user.getId(); + Long wishId = wishlist.getId(); + + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + when(wishlistRepository.findByIdAndUserId(wishId, userId)).thenReturn(Optional.of(wishlist)); + + // when + wishListService.deleteWish(userId, wishId); + + // then + verify(wishlistRepository).findByIdAndUserId(wishId, userId); + verify(wishlistRepository).delete(wishlist); + } +} \ No newline at end of file From 68ff6e7b28e77367dda3bfe1f9857b300c3cd6e3 Mon Sep 17 00:00:00 2001 From: humpose Date: Sat, 9 Nov 2024 17:41:23 +0900 Subject: [PATCH 03/38] =?UTF-8?q?refactor:=20=EC=BD=94=EC=B9=98=EB=8B=98?= =?UTF-8?q?=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98=EC=98=81=ED=95=B4=EC=84=9C=20?= =?UTF-8?q?=EC=98=81=EC=86=8D=EC=84=B1=20=EC=BB=A8=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=B3=80=EA=B2=BD=20=EA=B0=90=EC=A7=80=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=ED=99=9C=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/jeje/work/aeatbe/entity/Wishlist.java | 1 + src/main/java/jeje/work/aeatbe/service/WishListService.java | 3 +-- src/test/java/jeje/work/aeatbe/WishListServiceTest.java | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/entity/Wishlist.java b/src/main/java/jeje/work/aeatbe/entity/Wishlist.java index 1fb5f8ef..b5e0a69e 100644 --- a/src/main/java/jeje/work/aeatbe/entity/Wishlist.java +++ b/src/main/java/jeje/work/aeatbe/entity/Wishlist.java @@ -11,6 +11,7 @@ @Index(name = "idx_product_id", columnList = "product_id") }) @Getter +@Setter @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor public class Wishlist extends BaseEntity{ diff --git a/src/main/java/jeje/work/aeatbe/service/WishListService.java b/src/main/java/jeje/work/aeatbe/service/WishListService.java index 64ffb7ff..ea12d5c5 100644 --- a/src/main/java/jeje/work/aeatbe/service/WishListService.java +++ b/src/main/java/jeje/work/aeatbe/service/WishListService.java @@ -93,8 +93,7 @@ public void updateWish(Long loginUserId, Long wishId, Long newProductId) { Product newProduct = productMapper.toEntity(productService.getProductDTO(newProductId), true); - Wishlist updatedWishlist = new Wishlist(existingWishlist.getId(), existingWishlist.getUser(), newProduct); - wishlistRepository.save(updatedWishlist); + existingWishlist.setProduct(newProduct); } /** diff --git a/src/test/java/jeje/work/aeatbe/WishListServiceTest.java b/src/test/java/jeje/work/aeatbe/WishListServiceTest.java index 779ed4a6..0dc6868f 100644 --- a/src/test/java/jeje/work/aeatbe/WishListServiceTest.java +++ b/src/test/java/jeje/work/aeatbe/WishListServiceTest.java @@ -157,7 +157,6 @@ void updateWish_Success() { // then verify(wishlistRepository).findByIdAndUserId(wishId, userId); verify(productService).getProductDTO(newProductId); - verify(wishlistRepository).save(any(Wishlist.class)); } @Test From 259b4f4b7080a124e63f3d0fb25587d6649b05ea Mon Sep 17 00:00:00 2001 From: humpose Date: Sat, 9 Nov 2024 21:59:04 +0900 Subject: [PATCH 04/38] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=20=EC=A3=BC=EC=84=9D=20=EB=B3=B5=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../work/aeatbe/service/ArticleService.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/main/java/jeje/work/aeatbe/service/ArticleService.java b/src/main/java/jeje/work/aeatbe/service/ArticleService.java index c3396e27..5ed94a31 100644 --- a/src/main/java/jeje/work/aeatbe/service/ArticleService.java +++ b/src/main/java/jeje/work/aeatbe/service/ArticleService.java @@ -27,6 +27,12 @@ public class ArticleService { private final ArticleRepository articleRepository; + /** + * 새로운 칼럼을 데이터베이스에 저장 + * + * @param articleDTO 생성할 칼럼의 정보가 포함된 DTO + * @return 생성된 칼럼의 DTO + */ public ArticleDTO createArticle(ArticleDTO articleDTO) { Article article = Article.builder() .title(articleDTO.title()) @@ -42,6 +48,15 @@ public ArticleDTO createArticle(ArticleDTO articleDTO) { return mapToDTO(article); } + /** + * 필터링 및 페이지네이션이 적용된 칼럼 목록 반환 + * + * @param category 칼럼의 카테고리 + * @param title 칼럼의 제목 + * @param subtitle 칼럼의 소제목 + * @param pageable 페이지네이션 정보 + * @return 필터링된 칼럼 목록과 페이지 정보가 포함된 DTO + */ public ArticleListResponseDTO getArticles(String category, String title, String subtitle, Pageable pageable) { Page
articlePage = applyFilters(category, title, subtitle, pageable); @@ -57,11 +72,24 @@ public ArticleListResponseDTO getArticles(String category, String title, String return new ArticleListResponseDTO(columns, pageInfo); } + /** + * 특정 칼럼 반환 + * + * @param id 반환할 칼럼의 ID + * @return 요청된 칼럼의 세부 정보가 포함된 DTO + */ public ArticleResponseDTO getArticleById(Long id) { Article article = findArticle(id); return mapToResponseDTO(article); } + /** + * 칼럼 업데이트 + * + * @param id 업데이트할 칼럼의 ID + * @param articleDTO 업데이트할 내용이 담긴 DTO + * @return 업데이트된 칼럼의 DTO + */ public ArticleDTO updateArticle(Long id, ArticleDTO articleDTO) { Article existingArticle = findArticle(id); @@ -80,6 +108,11 @@ public ArticleDTO updateArticle(Long id, ArticleDTO articleDTO) { return mapToDTO(existingArticle); } + /** + * 칼럼 삭제 + * + * @param id 삭제할 칼럼의 ID + */ public void deleteArticle(Long id) { Article article = findArticle(id); articleRepository.delete(article); From e47f989c014fcd785922723a40762bd8f3e93656 Mon Sep 17 00:00:00 2001 From: humpose Date: Sat, 9 Nov 2024 23:56:24 +0900 Subject: [PATCH 05/38] =?UTF-8?q?refactor:=20mapper=EB=A1=9C=20dto<->entit?= =?UTF-8?q?y=20=EB=B3=80=ED=99=98=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aeatbe/mapper/article/ArticleMapper.java | 52 +++++++++++++++++++ .../work/aeatbe/service/ArticleService.java | 38 +++----------- 2 files changed, 60 insertions(+), 30 deletions(-) create mode 100644 src/main/java/jeje/work/aeatbe/mapper/article/ArticleMapper.java diff --git a/src/main/java/jeje/work/aeatbe/mapper/article/ArticleMapper.java b/src/main/java/jeje/work/aeatbe/mapper/article/ArticleMapper.java new file mode 100644 index 00000000..36593322 --- /dev/null +++ b/src/main/java/jeje/work/aeatbe/mapper/article/ArticleMapper.java @@ -0,0 +1,52 @@ +package jeje.work.aeatbe.mapper.article; + +import java.util.Arrays; +import jeje.work.aeatbe.dto.article.ArticleDTO; +import jeje.work.aeatbe.dto.article.ArticleResponseDTO; +import jeje.work.aeatbe.entity.Article; +import jeje.work.aeatbe.utility.ArticleUtil; +import org.springframework.stereotype.Component; + +/* +칼럼 매퍼 + */ +@Component +public class ArticleMapper { + + /** + * Entity -> DTO + * @param article + * @return + */ + public ArticleDTO toDTO(Article article) { + return ArticleDTO.builder() + .id(article.getId()) + .title(article.getTitle()) + .date(article.getDate()) + .author(article.getAuthor()) + .tags(article.getTags()) + .content(article.getContent()) + .thumbnailUrl(article.getThumbnailUrl()) + .likes(article.getLikes()) + .build(); + } + + /** + * Entity -> ResponseDTO + * @param article + * @return + */ + public ArticleResponseDTO toResponseDTO(Article article) { + return ArticleResponseDTO.builder() + .id(article.getId()) + .title(article.getTitle()) + .imgurl(article.getThumbnailUrl()) + .createdAt(article.getDate()) + .auth(article.getAuthor()) + .keyword(Arrays.asList(article.getTags().split(","))) + .content(ArticleUtil.extractContentList(article.getContent())) + .subtitle(ArticleUtil.extractSubtitle(article.getContent())) + .build(); + } + +} diff --git a/src/main/java/jeje/work/aeatbe/service/ArticleService.java b/src/main/java/jeje/work/aeatbe/service/ArticleService.java index 5ed94a31..0e029cb4 100644 --- a/src/main/java/jeje/work/aeatbe/service/ArticleService.java +++ b/src/main/java/jeje/work/aeatbe/service/ArticleService.java @@ -12,6 +12,8 @@ import jeje.work.aeatbe.entity.User; import jeje.work.aeatbe.exception.ColumnNotFoundException; import jeje.work.aeatbe.exception.UserNotFoundException; +import jeje.work.aeatbe.mapper.allergyCategory.AllergyCategoryMapper; +import jeje.work.aeatbe.mapper.article.ArticleMapper; import jeje.work.aeatbe.repository.ArticleRepository; import jeje.work.aeatbe.utility.ArticleUtil; import lombok.RequiredArgsConstructor; @@ -26,6 +28,8 @@ public class ArticleService { private final ArticleRepository articleRepository; + private final ArticleMapper articleMapper; + /** * 새로운 칼럼을 데이터베이스에 저장 @@ -45,7 +49,7 @@ public ArticleDTO createArticle(ArticleDTO articleDTO) { .build(); article = articleRepository.save(article); - return mapToDTO(article); + return articleMapper.toDTO(article); } /** @@ -66,7 +70,7 @@ public ArticleListResponseDTO getArticles(String category, String title, String .build(); List columns = articlePage.getContent().stream() - .map(this::mapToResponseDTO) + .map(articleMapper::toResponseDTO) .collect(Collectors.toList()); return new ArticleListResponseDTO(columns, pageInfo); @@ -80,7 +84,7 @@ public ArticleListResponseDTO getArticles(String category, String title, String */ public ArticleResponseDTO getArticleById(Long id) { Article article = findArticle(id); - return mapToResponseDTO(article); + return articleMapper.toResponseDTO(article); } /** @@ -105,7 +109,7 @@ public ArticleDTO updateArticle(Long id, ArticleDTO articleDTO) { .build(); articleRepository.save(existingArticle); - return mapToDTO(existingArticle); + return articleMapper.toDTO(existingArticle); } /** @@ -138,31 +142,5 @@ private Article findArticle(Long id) { return articleRepository.findById(id) .orElseThrow(() -> new ColumnNotFoundException("Article with id " + id + " not found")); } - - private ArticleDTO mapToDTO(Article article) { - return ArticleDTO.builder() - .id(article.getId()) - .title(article.getTitle()) - .date(article.getDate()) - .author(article.getAuthor()) - .tags(article.getTags()) - .content(article.getContent()) - .thumbnailUrl(article.getThumbnailUrl()) - .likes(article.getLikes()) - .build(); - } - - private ArticleResponseDTO mapToResponseDTO(Article article) { - return ArticleResponseDTO.builder() - .id(article.getId()) - .title(article.getTitle()) - .imgurl(article.getThumbnailUrl()) - .createdAt(article.getDate()) - .auth(article.getAuthor()) - .keyword(Arrays.asList(article.getTags().split(","))) - .content(ArticleUtil.extractContentList(article.getContent())) - .subtitle(ArticleUtil.extractSubtitle(article.getContent())) - .build(); - } } From ccfed8db6dea48eee081942a9bdf6d58107ab60a Mon Sep 17 00:00:00 2001 From: humpose Date: Sat, 9 Nov 2024 23:56:51 +0900 Subject: [PATCH 06/38] =?UTF-8?q?refactor:=20=EB=B6=88=EB=B3=80=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20setter=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=B0=8F=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=B3=80=EA=B2=BD=20=EC=B7=A8?= =?UTF-8?q?=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/jeje/work/aeatbe/entity/Wishlist.java | 1 - src/main/java/jeje/work/aeatbe/service/WishListService.java | 3 ++- src/test/java/jeje/work/aeatbe/WishListServiceTest.java | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/entity/Wishlist.java b/src/main/java/jeje/work/aeatbe/entity/Wishlist.java index b5e0a69e..1fb5f8ef 100644 --- a/src/main/java/jeje/work/aeatbe/entity/Wishlist.java +++ b/src/main/java/jeje/work/aeatbe/entity/Wishlist.java @@ -11,7 +11,6 @@ @Index(name = "idx_product_id", columnList = "product_id") }) @Getter -@Setter @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor public class Wishlist extends BaseEntity{ diff --git a/src/main/java/jeje/work/aeatbe/service/WishListService.java b/src/main/java/jeje/work/aeatbe/service/WishListService.java index ea12d5c5..64ffb7ff 100644 --- a/src/main/java/jeje/work/aeatbe/service/WishListService.java +++ b/src/main/java/jeje/work/aeatbe/service/WishListService.java @@ -93,7 +93,8 @@ public void updateWish(Long loginUserId, Long wishId, Long newProductId) { Product newProduct = productMapper.toEntity(productService.getProductDTO(newProductId), true); - existingWishlist.setProduct(newProduct); + Wishlist updatedWishlist = new Wishlist(existingWishlist.getId(), existingWishlist.getUser(), newProduct); + wishlistRepository.save(updatedWishlist); } /** diff --git a/src/test/java/jeje/work/aeatbe/WishListServiceTest.java b/src/test/java/jeje/work/aeatbe/WishListServiceTest.java index 0dc6868f..779ed4a6 100644 --- a/src/test/java/jeje/work/aeatbe/WishListServiceTest.java +++ b/src/test/java/jeje/work/aeatbe/WishListServiceTest.java @@ -157,6 +157,7 @@ void updateWish_Success() { // then verify(wishlistRepository).findByIdAndUserId(wishId, userId); verify(productService).getProductDTO(newProductId); + verify(wishlistRepository).save(any(Wishlist.class)); } @Test From 86b5dcb858da2a9b8f7a42ecec3f6c5537794e00 Mon Sep 17 00:00:00 2001 From: humpose Date: Sat, 9 Nov 2024 23:57:28 +0900 Subject: [PATCH 07/38] =?UTF-8?q?refactor:=20=EB=AF=B8=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=20import=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/jeje/work/aeatbe/service/ArticleService.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/service/ArticleService.java b/src/main/java/jeje/work/aeatbe/service/ArticleService.java index 0e029cb4..049cbf13 100644 --- a/src/main/java/jeje/work/aeatbe/service/ArticleService.java +++ b/src/main/java/jeje/work/aeatbe/service/ArticleService.java @@ -1,26 +1,18 @@ package jeje.work.aeatbe.service; -import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import jeje.work.aeatbe.dto.article.ArticleDTO; import jeje.work.aeatbe.dto.article.ArticleListResponseDTO; import jeje.work.aeatbe.dto.article.ArticleResponseDTO; -import jeje.work.aeatbe.dto.article.ContentDTO; import jeje.work.aeatbe.dto.article.PageInfoDTO; import jeje.work.aeatbe.entity.Article; -import jeje.work.aeatbe.entity.User; import jeje.work.aeatbe.exception.ColumnNotFoundException; -import jeje.work.aeatbe.exception.UserNotFoundException; -import jeje.work.aeatbe.mapper.allergyCategory.AllergyCategoryMapper; import jeje.work.aeatbe.mapper.article.ArticleMapper; import jeje.work.aeatbe.repository.ArticleRepository; -import jeje.work.aeatbe.utility.ArticleUtil; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; @Service From 5e7ee54aa54c7afdef1800212b4c7d11ae48de96 Mon Sep 17 00:00:00 2001 From: humpose Date: Sun, 10 Nov 2024 01:23:08 +0900 Subject: [PATCH 08/38] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20id=EB=B0=98=ED=99=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/jeje/work/aeatbe/mapper/article/ArticleMapper.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/jeje/work/aeatbe/mapper/article/ArticleMapper.java b/src/main/java/jeje/work/aeatbe/mapper/article/ArticleMapper.java index 36593322..07ec97e3 100644 --- a/src/main/java/jeje/work/aeatbe/mapper/article/ArticleMapper.java +++ b/src/main/java/jeje/work/aeatbe/mapper/article/ArticleMapper.java @@ -20,7 +20,6 @@ public class ArticleMapper { */ public ArticleDTO toDTO(Article article) { return ArticleDTO.builder() - .id(article.getId()) .title(article.getTitle()) .date(article.getDate()) .author(article.getAuthor()) From 42257bb62e1b320f3f087f67e9d48a1421c60ace Mon Sep 17 00:00:00 2001 From: humpose Date: Sun, 10 Nov 2024 03:29:51 +0900 Subject: [PATCH 09/38] =?UTF-8?q?fix:=20=EC=9C=84=EC=8B=9C=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=98=88=EC=99=B8=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=B4=20=EC=A0=81=EC=A0=88=ED=95=9C=20httpStatus=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java b/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java index 9043d8be..91ec9973 100644 --- a/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java +++ b/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java @@ -35,7 +35,7 @@ public ResponseEntity illegalStateException(IllegalStateException e) { @ExceptionHandler(WishlistNotFoundException.class) public ResponseEntity wishlistNotFoundException(WishlistNotFoundException e) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage()); } @ExceptionHandler(TokenExpException.class) From d7e14083c0db971612468b820b2620a8d8ae7819 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Sat, 9 Nov 2024 17:19:41 +0900 Subject: [PATCH 10/38] feat:sameSite:None --- src/main/java/jeje/work/aeatbe/service/UserService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/service/UserService.java b/src/main/java/jeje/work/aeatbe/service/UserService.java index 3e57dafc..81e1f9ed 100644 --- a/src/main/java/jeje/work/aeatbe/service/UserService.java +++ b/src/main/java/jeje/work/aeatbe/service/UserService.java @@ -163,7 +163,7 @@ public HttpHeaders setCookie(TokenResponseDTO tokenResponseDTO){ .path("/") .maxAge(3600) .domain(".aeat.jeje.work") - .sameSite("LAX") + .sameSite("None") .build(); ResponseCookie accessCookie = ResponseCookie.from("Authorization-accessToken", tokenResponseDTO.accessToken()) .httpOnly(true) @@ -171,7 +171,7 @@ public HttpHeaders setCookie(TokenResponseDTO tokenResponseDTO){ .path("/") .maxAge(3600) .domain(".aeat.jeje.work") - .sameSite("LAX") + .sameSite("None") .build(); HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.SET_COOKIE, accessCookie.toString()); From 5a98cc6eff45d019b88760cd2298573605066625 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Sun, 10 Nov 2024 01:00:27 +0900 Subject: [PATCH 11/38] =?UTF-8?q?fix:=20=EB=A6=AC=ED=94=84=EB=A0=88?= =?UTF-8?q?=EC=8B=9C=ED=86=A0=ED=81=B0=20=EC=BF=A0=ED=82=A4=20=EB=A7=8C?= =?UTF-8?q?=EB=A3=8C=EC=8B=B2=EA=B0=84=20=EC=9E=AC=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/jeje/work/aeatbe/service/UserService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/jeje/work/aeatbe/service/UserService.java b/src/main/java/jeje/work/aeatbe/service/UserService.java index 81e1f9ed..72ee9e80 100644 --- a/src/main/java/jeje/work/aeatbe/service/UserService.java +++ b/src/main/java/jeje/work/aeatbe/service/UserService.java @@ -161,7 +161,7 @@ public HttpHeaders setCookie(TokenResponseDTO tokenResponseDTO){ .httpOnly(true) .secure(true) .path("/") - .maxAge(3600) + .maxAge(3600*24*14) .domain(".aeat.jeje.work") .sameSite("None") .build(); From abdf3eb445ef4381b5f99a6202dbb42b93d074d0 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Sun, 10 Nov 2024 02:06:36 +0900 Subject: [PATCH 12/38] fix:ExceptionHandler Return raw type --- .../aeatbe/exception/GlobalExceptionHandler.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java b/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java index cac7c2e8..d945812a 100644 --- a/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java +++ b/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java @@ -19,12 +19,20 @@ public ResponseEntity illegalArgumentException(IllegalArgumentException e) { } @ExceptionHandler(IllegalStateException.class) - public ResponseEntity illegalStateException(IllegalStateException e) { + public ResponseEntity illegalStateException(IllegalStateException e) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); + } + + @ExceptionHandler(WishlistNotFoundException.class) + public ResponseEntity wishlistNotFoundException(WishlistNotFoundException e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); } @ExceptionHandler(TokenExpException.class) - public ResponseEntity tokenExpException(TokenExpException e) { + public ResponseEntity tokenExpException(TokenExpException e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); } + + + } From 16b5f7d2b7512870069a9250e7c7858db0b61ca3 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Sun, 10 Nov 2024 02:08:50 +0900 Subject: [PATCH 13/38] fix:wishListNotFoundException httpStatus type --- .../java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java b/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java index d945812a..e08eb505 100644 --- a/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java +++ b/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java @@ -25,7 +25,7 @@ public ResponseEntity illegalStateException(IllegalStateException e) { @ExceptionHandler(WishlistNotFoundException.class) public ResponseEntity wishlistNotFoundException(WishlistNotFoundException e) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage()); } @ExceptionHandler(TokenExpException.class) From 2e304f3f732418d9939087a59cb643a1ade95333 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Sun, 10 Nov 2024 04:33:35 +0900 Subject: [PATCH 14/38] feat: redis config --- build.gradle.kts | 1 - .../jeje/work/aeatbe/config/RedisConfig.java | 46 +++++++++++++++++++ src/main/resources/application.yml | 5 ++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/main/java/jeje/work/aeatbe/config/RedisConfig.java diff --git a/build.gradle.kts b/build.gradle.kts index 00e29750..630e38b9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -46,7 +46,6 @@ dependencies { //redis implementation("org.springframework.boot:spring-boot-starter-data-redis") - implementation("org.springframework.boot:spring-boot-starter-data-redis-reactive") } tasks.withType { diff --git a/src/main/java/jeje/work/aeatbe/config/RedisConfig.java b/src/main/java/jeje/work/aeatbe/config/RedisConfig.java new file mode 100644 index 00000000..babd99a9 --- /dev/null +++ b/src/main/java/jeje/work/aeatbe/config/RedisConfig.java @@ -0,0 +1,46 @@ +package jeje.work.aeatbe.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +@EnableRedisRepositories +public class RedisConfig { + + @Value("${spring.data.redis.host}") + private String host; + + @Value("${spring.data.redis.port}") + private int port; + + @Value("${spring.data.redis.password}") + private String password; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + return new LettuceConnectionFactory(host, port); + + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + + redisTemplate.setDefaultSerializer(new StringRedisSerializer()); + + return redisTemplate; + } + + + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e202b856..97c51b99 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -36,10 +36,14 @@ spring: file: ./docker/compose.yml skip: in-tests: false + + data: redis: host: ${REDIS_HOST} port: ${REDIS_PORT} + password: ${REDIS_PASSWORD} + kakao: client_id: ${KAKAO_CLIENT_ID} @@ -54,3 +58,4 @@ haccp: default: image: https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbafebf27-4e21-4ed6-99e1-983eb90ad9c0%2F75c38d1f-215b-4536-a401-18cf179bf119%2Fimage.png?table=block&id=137a63f1-9420-8046-9cb6-d218151fd876&cache=v2 + From 2a0d0de23bdab3fe491a12de0b4079aacbe30f7f Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Sun, 10 Nov 2024 06:09:21 +0900 Subject: [PATCH 15/38] feat: redis config --- .../jeje/work/aeatbe/config/RedisConfig.java | 36 ++++++++++++------- .../jeje/work/aeatbe/config/WebConfig.java | 2 +- src/main/resources/application.yml | 2 ++ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/config/RedisConfig.java b/src/main/java/jeje/work/aeatbe/config/RedisConfig.java index babd99a9..fcaa7382 100644 --- a/src/main/java/jeje/work/aeatbe/config/RedisConfig.java +++ b/src/main/java/jeje/work/aeatbe/config/RedisConfig.java @@ -4,6 +4,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; @@ -22,24 +23,33 @@ public class RedisConfig { @Value("${spring.data.redis.password}") private String password; + @Value("${spring.data.redis.username}") + private String username; + @Bean public RedisConnectionFactory redisConnectionFactory() { - return new LettuceConnectionFactory(host, port); + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(host, port); + config.setUsername(username); + config.setPassword(password); + return new LettuceConnectionFactory(config); } - @Bean - public RedisTemplate redisTemplate() { - RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setConnectionFactory(redisConnectionFactory()); - - redisTemplate.setKeySerializer(new StringRedisSerializer()); - redisTemplate.setValueSerializer(new StringRedisSerializer()); - - redisTemplate.setDefaultSerializer(new StringRedisSerializer()); - - return redisTemplate; - } +// @Bean +// public RedisTemplate redisTemplate() { +// RedisTemplate redisTemplate = new RedisTemplate<>(); +// redisTemplate.setConnectionFactory(redisConnectionFactory()); +// +// redisTemplate.setKeySerializer(new StringRedisSerializer()); +// redisTemplate.setValueSerializer(new StringRedisSerializer()); +// +// redisTemplate.setHashKeySerializer(new StringRedisSerializer()); +// redisTemplate.setHashValueSerializer(new StringRedisSerializer()); +// +// redisTemplate.setDefaultSerializer(new StringRedisSerializer()); +// +// return redisTemplate; +// } diff --git a/src/main/java/jeje/work/aeatbe/config/WebConfig.java b/src/main/java/jeje/work/aeatbe/config/WebConfig.java index 3c19e0ba..e8563f0c 100644 --- a/src/main/java/jeje/work/aeatbe/config/WebConfig.java +++ b/src/main/java/jeje/work/aeatbe/config/WebConfig.java @@ -38,7 +38,7 @@ public void addArgumentResolvers(List argumentRes @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") - .allowedOriginPatterns("https://aeat.jeje.work", "http://localhost") + .allowedOriginPatterns("https://aeat.jeje.work", "http://localhost","*") .allowedMethods("*") .allowedHeaders("*") .allowCredentials(true) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 97c51b99..bc20a596 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -42,7 +42,9 @@ spring: redis: host: ${REDIS_HOST} port: ${REDIS_PORT} + url: ${REDIS_URL} password: ${REDIS_PASSWORD} + username: ${REDIS_ID} kakao: From ff5e36874cc47a5c8e095e753687e28f910b8924 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Sun, 10 Nov 2024 17:51:10 +0900 Subject: [PATCH 16/38] =?UTF-8?q?fix:=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jeje/work/aeatbe/entity/BlackList.java | 19 +++++++++++++++++++ .../aeatbe/interceptor/JwtInterceptor.java | 2 ++ 2 files changed, 21 insertions(+) create mode 100644 src/main/java/jeje/work/aeatbe/entity/BlackList.java diff --git a/src/main/java/jeje/work/aeatbe/entity/BlackList.java b/src/main/java/jeje/work/aeatbe/entity/BlackList.java new file mode 100644 index 00000000..8f56870a --- /dev/null +++ b/src/main/java/jeje/work/aeatbe/entity/BlackList.java @@ -0,0 +1,19 @@ +package jeje.work.aeatbe.entity; + +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.redis.core.RedisHash; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@RedisHash("blackList") +public class BlackList { + + @Id + private String token; + + private String id; +} diff --git a/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java b/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java index 18924f3d..effcf110 100644 --- a/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java +++ b/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java @@ -38,6 +38,8 @@ public boolean preHandle(final HttpServletRequest request, final HttpServletResp if(!userService.validateToken(token)){ throw new TokenException("올바르지 않은 토큰입니다."); } + + return true; From 50c38c89773cb0fc365e0da390474a5f1beef265 Mon Sep 17 00:00:00 2001 From: youcalste03 Date: Sun, 10 Nov 2024 19:41:19 +0900 Subject: [PATCH 17/38] feat:BlackListRepository --- .../jeje/work/aeatbe/repository/BlackListRepository.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java diff --git a/src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java b/src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java new file mode 100644 index 00000000..ab4e618a --- /dev/null +++ b/src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java @@ -0,0 +1,8 @@ +package jeje.work.aeatbe.repository; + +import jeje.work.aeatbe.entity.BlackList; +import org.springframework.data.repository.CrudRepository; + +public interface BlackListRepository extends CrudRepository { + +} From c8b0a0f5d5cb9837b504939002431e89dfbe0ea9 Mon Sep 17 00:00:00 2001 From: youcalste03 Date: Sun, 10 Nov 2024 20:43:31 +0900 Subject: [PATCH 18/38] feat:TokenService --- .../controller/KakaoAuthController.java | 5 +++-- .../jeje/work/aeatbe/entity/BlackList.java | 4 ++-- .../work/aeatbe/service/TokenService.java | 21 +++++++++++++++++++ src/main/resources/application.yml | 6 ++++-- 4 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 src/main/java/jeje/work/aeatbe/service/TokenService.java diff --git a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java index 8301101e..a5d4e1b2 100644 --- a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java +++ b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java @@ -16,6 +16,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -53,7 +54,7 @@ public ResponseEntity getAccessToken(@RequestParam String code) { KakaoTokenResponsed token = kakaoService.getKakaoTokenResponse(code); TokenResponseDTO tokenResponseDto = kakaoService.login(token.accessToken(), token.refreshToken()); HttpHeaders httpHeaders = userService.setCookie(tokenResponseDto); - return ResponseEntity.ok().headers(httpHeaders).build(); + return ResponseEntity.ok().headers(httpHeaders).body(tokenResponseDto); } /** @@ -63,7 +64,7 @@ public ResponseEntity getAccessToken(@RequestParam String code) { * @throws IOException */ @PostMapping("/logout") - public void logout(HttpServletResponse response, @LoginUser LoginUserInfo loginUserInfo) throws IOException{ + public void logout(HttpServletResponse response, @RequestHeader("Authorization") String token, @LoginUser LoginUserInfo loginUserInfo) throws IOException{ String url = kakaoProperties.logoutUrl() + "?client_id=" + kakaoProperties.clientId() + "&logout_redirect_uri=" + kakaoProperties.logoutRedirectUrl(); LogoutResponseDto logoutResponseDto = kakaoService.logout(loginUserInfo.userId()); diff --git a/src/main/java/jeje/work/aeatbe/entity/BlackList.java b/src/main/java/jeje/work/aeatbe/entity/BlackList.java index 8f56870a..3ac1a4eb 100644 --- a/src/main/java/jeje/work/aeatbe/entity/BlackList.java +++ b/src/main/java/jeje/work/aeatbe/entity/BlackList.java @@ -9,11 +9,11 @@ @Getter @NoArgsConstructor @AllArgsConstructor -@RedisHash("blackList") +@RedisHash(value = "blackList" , timeToLive = 3600) public class BlackList { @Id private String token; - private String id; + private Long id; } diff --git a/src/main/java/jeje/work/aeatbe/service/TokenService.java b/src/main/java/jeje/work/aeatbe/service/TokenService.java new file mode 100644 index 00000000..4f7cc50b --- /dev/null +++ b/src/main/java/jeje/work/aeatbe/service/TokenService.java @@ -0,0 +1,21 @@ +package jeje.work.aeatbe.service; + +import jeje.work.aeatbe.entity.BlackList; +import jeje.work.aeatbe.repository.BlackListRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class TokenService { + + private final BlackListRepository blackListRepository; + + + public void addBlackList (String accessToken, Long userId){ + BlackList blackList = new BlackList(accessToken, userId); + blackListRepository.save(blackList); + } + + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index bc20a596..f53a2247 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -49,10 +49,12 @@ spring: kakao: client_id: ${KAKAO_CLIENT_ID} - redirect_url: https://${FRONTEND_DOMAIN}/login/redirect +# redirect_url: https://${FRONTEND_DOMAIN}/login/redirect + redirect_url: http://localhost:8080/api/users/callback auth_url: https://kauth.kakao.com/oauth/authorize logout_url: https://kauth.kakao.com/oauth/logout - logout_redirect_url: https://${FRONTEND_DOMAIN}/logout/redirect +# logout_redirect_url: https://${FRONTEND_DOMAIN}/logout/redirect + logout_redirect_url: http://localhost:8080/api/users/logoutWithKakao/callback haccp: service_key : ${SERVICE_KEY} From f9dc815f5f13080aaeb80cf4fc5445c6a606ad93 Mon Sep 17 00:00:00 2001 From: youcalste03 Date: Sun, 10 Nov 2024 21:19:51 +0900 Subject: [PATCH 19/38] =?UTF-8?q?feat:=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83?= =?UTF-8?q?=EC=8B=9C=20=EB=B8=94=EB=9E=99=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jeje/work/aeatbe/config/RedisConfig.java | 30 +++++++++---------- .../controller/KakaoAuthController.java | 3 ++ .../jeje/work/aeatbe/entity/BlackList.java | 3 +- .../work/aeatbe/service/TokenService.java | 18 ++++++++++- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/config/RedisConfig.java b/src/main/java/jeje/work/aeatbe/config/RedisConfig.java index fcaa7382..872dae17 100644 --- a/src/main/java/jeje/work/aeatbe/config/RedisConfig.java +++ b/src/main/java/jeje/work/aeatbe/config/RedisConfig.java @@ -35,21 +35,21 @@ public RedisConnectionFactory redisConnectionFactory() { } -// @Bean -// public RedisTemplate redisTemplate() { -// RedisTemplate redisTemplate = new RedisTemplate<>(); -// redisTemplate.setConnectionFactory(redisConnectionFactory()); -// -// redisTemplate.setKeySerializer(new StringRedisSerializer()); -// redisTemplate.setValueSerializer(new StringRedisSerializer()); -// -// redisTemplate.setHashKeySerializer(new StringRedisSerializer()); -// redisTemplate.setHashValueSerializer(new StringRedisSerializer()); -// -// redisTemplate.setDefaultSerializer(new StringRedisSerializer()); -// -// return redisTemplate; -// } + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashValueSerializer(new StringRedisSerializer()); + + redisTemplate.setDefaultSerializer(new StringRedisSerializer()); + + return redisTemplate; + } diff --git a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java index a5d4e1b2..8830f6a2 100644 --- a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java +++ b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java @@ -9,6 +9,7 @@ import jeje.work.aeatbe.dto.user.TokenResponseDTO; import jeje.work.aeatbe.dto.user.LoginUserInfo; import jeje.work.aeatbe.service.KakaoService; +import jeje.work.aeatbe.service.TokenService; import jeje.work.aeatbe.service.UserService; import jeje.work.aeatbe.utility.JwtUtil; import lombok.RequiredArgsConstructor; @@ -30,6 +31,7 @@ public class KakaoAuthController { private final JwtUtil jwtUtil; private final KakaoProperties kakaoProperties; private final KakaoService kakaoService; + private final TokenService tokenService; /** * 카카오 로그인페이지로 리다이렉션 @@ -67,6 +69,7 @@ public ResponseEntity getAccessToken(@RequestParam String code) { public void logout(HttpServletResponse response, @RequestHeader("Authorization") String token, @LoginUser LoginUserInfo loginUserInfo) throws IOException{ String url = kakaoProperties.logoutUrl() + "?client_id=" + kakaoProperties.clientId() + "&logout_redirect_uri=" + kakaoProperties.logoutRedirectUrl(); + tokenService.addBlackList(token, loginUserInfo.userId()); LogoutResponseDto logoutResponseDto = kakaoService.logout(loginUserInfo.userId()); response.sendRedirect(url); } diff --git a/src/main/java/jeje/work/aeatbe/entity/BlackList.java b/src/main/java/jeje/work/aeatbe/entity/BlackList.java index 3ac1a4eb..d306b52d 100644 --- a/src/main/java/jeje/work/aeatbe/entity/BlackList.java +++ b/src/main/java/jeje/work/aeatbe/entity/BlackList.java @@ -1,9 +1,10 @@ package jeje.work.aeatbe.entity; -import jakarta.persistence.Id; + import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; import org.springframework.data.redis.core.RedisHash; @Getter diff --git a/src/main/java/jeje/work/aeatbe/service/TokenService.java b/src/main/java/jeje/work/aeatbe/service/TokenService.java index 4f7cc50b..3297aafe 100644 --- a/src/main/java/jeje/work/aeatbe/service/TokenService.java +++ b/src/main/java/jeje/work/aeatbe/service/TokenService.java @@ -11,11 +11,27 @@ public class TokenService { private final BlackListRepository blackListRepository; - + /** + * 로그아웃된 엑세스 토큰을 블랙 리스트에 등록 + * @param accessToken + * @param userId + */ public void addBlackList (String accessToken, Long userId){ BlackList blackList = new BlackList(accessToken, userId); blackListRepository.save(blackList); } + /** + * 토큰의 Bearer prefix 제거 + * @param token + * @return prefix없는 순수 토큰 + */ + public String removePrefix(String token){ + if(token.startsWith("Bearer ")){ + return token.substring(7); + } + return token; + } + } From ee0507e693f9dfa41fa815ca854ff34bcf423142 Mon Sep 17 00:00:00 2001 From: youcalste03 Date: Sun, 10 Nov 2024 21:37:57 +0900 Subject: [PATCH 20/38] =?UTF-8?q?refactor:=20cookie=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=20=EC=B7=A8=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jeje/work/aeatbe/controller/KakaoAuthController.java | 6 +++--- .../java/jeje/work/aeatbe/controller/TokenController.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java index 8830f6a2..65900a22 100644 --- a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java +++ b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java @@ -52,11 +52,11 @@ public void redirectKakaoLogin(HttpServletResponse response) throws IOException * @return httpHeader(Cookie) */ @GetMapping("/callback") - public ResponseEntity getAccessToken(@RequestParam String code) { + public ResponseEntity getAccessToken(@RequestParam String code) { KakaoTokenResponsed token = kakaoService.getKakaoTokenResponse(code); TokenResponseDTO tokenResponseDto = kakaoService.login(token.accessToken(), token.refreshToken()); HttpHeaders httpHeaders = userService.setCookie(tokenResponseDto); - return ResponseEntity.ok().headers(httpHeaders).body(tokenResponseDto); + return ResponseEntity.ok().body(tokenResponseDto); } /** @@ -69,7 +69,7 @@ public ResponseEntity getAccessToken(@RequestParam String code) { public void logout(HttpServletResponse response, @RequestHeader("Authorization") String token, @LoginUser LoginUserInfo loginUserInfo) throws IOException{ String url = kakaoProperties.logoutUrl() + "?client_id=" + kakaoProperties.clientId() + "&logout_redirect_uri=" + kakaoProperties.logoutRedirectUrl(); - tokenService.addBlackList(token, loginUserInfo.userId()); + tokenService.addBlackList(tokenService.removePrefix(token), loginUserInfo.userId()); LogoutResponseDto logoutResponseDto = kakaoService.logout(loginUserInfo.userId()); response.sendRedirect(url); } diff --git a/src/main/java/jeje/work/aeatbe/controller/TokenController.java b/src/main/java/jeje/work/aeatbe/controller/TokenController.java index 45ecb88d..7ddc3e3f 100644 --- a/src/main/java/jeje/work/aeatbe/controller/TokenController.java +++ b/src/main/java/jeje/work/aeatbe/controller/TokenController.java @@ -27,6 +27,6 @@ public class TokenController { public ResponseEntity refreshToken(@RequestBody RefreshTokenRequestDTO refreshTokenRequestDTO){ TokenResponseDTO tokenResponseDto = userService.reissueAccessToken(refreshTokenRequestDTO.refreshToken()); HttpHeaders httpHeaders = userService.setCookie(tokenResponseDto); - return ResponseEntity.ok().headers(httpHeaders).build(); + return ResponseEntity.ok().body(tokenResponseDto); } } From 2a02980c8938ac3b7b4bb7d1852db4078ac715a1 Mon Sep 17 00:00:00 2001 From: youcalste03 Date: Sun, 10 Nov 2024 23:52:58 +0900 Subject: [PATCH 21/38] feat: blackList --- .../java/jeje/work/aeatbe/config/WebConfig.java | 3 ++- .../aeatbe/controller/KakaoAuthController.java | 2 +- .../java/jeje/work/aeatbe/entity/BlackList.java | 2 +- .../work/aeatbe/interceptor/JwtInterceptor.java | 5 +++++ .../aeatbe/repository/BlackListRepository.java | 1 + .../jeje/work/aeatbe/service/TokenService.java | 17 ++++++++++++++++- 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/config/WebConfig.java b/src/main/java/jeje/work/aeatbe/config/WebConfig.java index e8563f0c..780e8f35 100644 --- a/src/main/java/jeje/work/aeatbe/config/WebConfig.java +++ b/src/main/java/jeje/work/aeatbe/config/WebConfig.java @@ -27,7 +27,8 @@ public void addInterceptors(InterceptorRegistry registry) { .addPathPatterns("/api/article/likes/**") .addPathPatterns("/api/users/logout/**") . addPathPatterns("/api/wishlist/**") - .addPathPatterns("/api/reviews/my/**"); + .addPathPatterns("/api/reviews/my/**") + .addPathPatterns("/api/users/info/**"); } @Override diff --git a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java index 65900a22..d7fedd7b 100644 --- a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java +++ b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java @@ -69,7 +69,7 @@ public ResponseEntity getAccessToken(@RequestParam String code public void logout(HttpServletResponse response, @RequestHeader("Authorization") String token, @LoginUser LoginUserInfo loginUserInfo) throws IOException{ String url = kakaoProperties.logoutUrl() + "?client_id=" + kakaoProperties.clientId() + "&logout_redirect_uri=" + kakaoProperties.logoutRedirectUrl(); - tokenService.addBlackList(tokenService.removePrefix(token), loginUserInfo.userId()); + tokenService.addBlackList(tokenService.removePrefix(token), loginUserInfo.userId().toString()); LogoutResponseDto logoutResponseDto = kakaoService.logout(loginUserInfo.userId()); response.sendRedirect(url); } diff --git a/src/main/java/jeje/work/aeatbe/entity/BlackList.java b/src/main/java/jeje/work/aeatbe/entity/BlackList.java index d306b52d..a1b1c4ac 100644 --- a/src/main/java/jeje/work/aeatbe/entity/BlackList.java +++ b/src/main/java/jeje/work/aeatbe/entity/BlackList.java @@ -16,5 +16,5 @@ public class BlackList { @Id private String token; - private Long id; + private String userId; } diff --git a/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java b/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java index effcf110..16075ecb 100644 --- a/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java +++ b/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java @@ -4,6 +4,7 @@ import jakarta.servlet.http.HttpServletResponse; import jeje.work.aeatbe.exception.TokenExpException; import jeje.work.aeatbe.exception.TokenException; +import jeje.work.aeatbe.service.TokenService; import jeje.work.aeatbe.service.UserService; import jeje.work.aeatbe.utility.JwtUtil; import lombok.AllArgsConstructor; @@ -17,6 +18,7 @@ public class JwtInterceptor implements HandlerInterceptor { private JwtUtil jwtUtil; private UserService userService; + private TokenService tokenService; @Override public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) @@ -29,6 +31,9 @@ public boolean preHandle(final HttpServletRequest request, final HttpServletResp String token = header.substring(7); + if(tokenService.isInBlackList(token)){ + throw new TokenException("이미로그아웃 된 사용자의 토큰입니다. 다시 로그인 해주세요"); + } if(jwtUtil.validTokenExpiration(token, true)) { throw new TokenExpException("만료된 토큰입니다."); diff --git a/src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java b/src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java index ab4e618a..d916ca8d 100644 --- a/src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java +++ b/src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java @@ -1,5 +1,6 @@ package jeje.work.aeatbe.repository; +import java.util.Optional; import jeje.work.aeatbe.entity.BlackList; import org.springframework.data.repository.CrudRepository; diff --git a/src/main/java/jeje/work/aeatbe/service/TokenService.java b/src/main/java/jeje/work/aeatbe/service/TokenService.java index 3297aafe..4d89b5df 100644 --- a/src/main/java/jeje/work/aeatbe/service/TokenService.java +++ b/src/main/java/jeje/work/aeatbe/service/TokenService.java @@ -1,8 +1,10 @@ package jeje.work.aeatbe.service; +import java.util.Optional; import jeje.work.aeatbe.entity.BlackList; import jeje.work.aeatbe.repository.BlackListRepository; import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @Service @@ -10,13 +12,15 @@ public class TokenService { private final BlackListRepository blackListRepository; + private final RedisTemplate redisTemplate; /** * 로그아웃된 엑세스 토큰을 블랙 리스트에 등록 * @param accessToken * @param userId */ - public void addBlackList (String accessToken, Long userId){ + public void addBlackList (String accessToken, String userId){ + accessToken = removePrefix(accessToken); BlackList blackList = new BlackList(accessToken, userId); blackListRepository.save(blackList); } @@ -33,5 +37,16 @@ public String removePrefix(String token){ return token; } + /** + * 블랙시스트에 있는지 확인 + * @param accessToken + * @return 유무 + */ + public boolean isInBlackList(String accessToken){ + Optional blackList = blackListRepository.findById(accessToken); + + return !blackList.isEmpty(); + } + } From 06cedc053c5b01e0924ae228852ad94eed154a33 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Sat, 9 Nov 2024 17:19:41 +0900 Subject: [PATCH 22/38] feat:sameSite:None --- src/main/java/jeje/work/aeatbe/service/UserService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/service/UserService.java b/src/main/java/jeje/work/aeatbe/service/UserService.java index 3e57dafc..81e1f9ed 100644 --- a/src/main/java/jeje/work/aeatbe/service/UserService.java +++ b/src/main/java/jeje/work/aeatbe/service/UserService.java @@ -163,7 +163,7 @@ public HttpHeaders setCookie(TokenResponseDTO tokenResponseDTO){ .path("/") .maxAge(3600) .domain(".aeat.jeje.work") - .sameSite("LAX") + .sameSite("None") .build(); ResponseCookie accessCookie = ResponseCookie.from("Authorization-accessToken", tokenResponseDTO.accessToken()) .httpOnly(true) @@ -171,7 +171,7 @@ public HttpHeaders setCookie(TokenResponseDTO tokenResponseDTO){ .path("/") .maxAge(3600) .domain(".aeat.jeje.work") - .sameSite("LAX") + .sameSite("None") .build(); HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.SET_COOKIE, accessCookie.toString()); From 6af155f7bafcf2afeb05ffdbf7cc8a3b466ae6e2 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Sun, 10 Nov 2024 01:00:27 +0900 Subject: [PATCH 23/38] =?UTF-8?q?fix:=20=EB=A6=AC=ED=94=84=EB=A0=88?= =?UTF-8?q?=EC=8B=9C=ED=86=A0=ED=81=B0=20=EC=BF=A0=ED=82=A4=20=EB=A7=8C?= =?UTF-8?q?=EB=A3=8C=EC=8B=B2=EA=B0=84=20=EC=9E=AC=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/jeje/work/aeatbe/service/UserService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/jeje/work/aeatbe/service/UserService.java b/src/main/java/jeje/work/aeatbe/service/UserService.java index 81e1f9ed..72ee9e80 100644 --- a/src/main/java/jeje/work/aeatbe/service/UserService.java +++ b/src/main/java/jeje/work/aeatbe/service/UserService.java @@ -161,7 +161,7 @@ public HttpHeaders setCookie(TokenResponseDTO tokenResponseDTO){ .httpOnly(true) .secure(true) .path("/") - .maxAge(3600) + .maxAge(3600*24*14) .domain(".aeat.jeje.work") .sameSite("None") .build(); From 0e3eba387a378dc87e4375aa27c1cf596f7992c4 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Sun, 10 Nov 2024 02:06:36 +0900 Subject: [PATCH 24/38] fix:ExceptionHandler Return raw type --- .../aeatbe/exception/GlobalExceptionHandler.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java b/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java index cac7c2e8..d945812a 100644 --- a/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java +++ b/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java @@ -19,12 +19,20 @@ public ResponseEntity illegalArgumentException(IllegalArgumentException e) { } @ExceptionHandler(IllegalStateException.class) - public ResponseEntity illegalStateException(IllegalStateException e) { + public ResponseEntity illegalStateException(IllegalStateException e) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); + } + + @ExceptionHandler(WishlistNotFoundException.class) + public ResponseEntity wishlistNotFoundException(WishlistNotFoundException e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); } @ExceptionHandler(TokenExpException.class) - public ResponseEntity tokenExpException(TokenExpException e) { + public ResponseEntity tokenExpException(TokenExpException e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); } + + + } From 362c3b6fec29ba002b808f052dc7edde1586ef9b Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Sun, 10 Nov 2024 02:08:50 +0900 Subject: [PATCH 25/38] fix:wishListNotFoundException httpStatus type --- .../java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java b/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java index d945812a..e08eb505 100644 --- a/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java +++ b/src/main/java/jeje/work/aeatbe/exception/GlobalExceptionHandler.java @@ -25,7 +25,7 @@ public ResponseEntity illegalStateException(IllegalStateException e) { @ExceptionHandler(WishlistNotFoundException.class) public ResponseEntity wishlistNotFoundException(WishlistNotFoundException e) { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage()); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage()); } @ExceptionHandler(TokenExpException.class) From 5d28de99a28766fdaf6f72b86d78774273e17645 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Sun, 10 Nov 2024 04:33:35 +0900 Subject: [PATCH 26/38] feat: redis config --- build.gradle.kts | 1 - .../jeje/work/aeatbe/config/RedisConfig.java | 46 +++++++++++++++++++ src/main/resources/application.yml | 5 ++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/main/java/jeje/work/aeatbe/config/RedisConfig.java diff --git a/build.gradle.kts b/build.gradle.kts index 00e29750..630e38b9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -46,7 +46,6 @@ dependencies { //redis implementation("org.springframework.boot:spring-boot-starter-data-redis") - implementation("org.springframework.boot:spring-boot-starter-data-redis-reactive") } tasks.withType { diff --git a/src/main/java/jeje/work/aeatbe/config/RedisConfig.java b/src/main/java/jeje/work/aeatbe/config/RedisConfig.java new file mode 100644 index 00000000..babd99a9 --- /dev/null +++ b/src/main/java/jeje/work/aeatbe/config/RedisConfig.java @@ -0,0 +1,46 @@ +package jeje.work.aeatbe.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +@EnableRedisRepositories +public class RedisConfig { + + @Value("${spring.data.redis.host}") + private String host; + + @Value("${spring.data.redis.port}") + private int port; + + @Value("${spring.data.redis.password}") + private String password; + + @Bean + public RedisConnectionFactory redisConnectionFactory() { + return new LettuceConnectionFactory(host, port); + + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + + redisTemplate.setDefaultSerializer(new StringRedisSerializer()); + + return redisTemplate; + } + + + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e202b856..97c51b99 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -36,10 +36,14 @@ spring: file: ./docker/compose.yml skip: in-tests: false + + data: redis: host: ${REDIS_HOST} port: ${REDIS_PORT} + password: ${REDIS_PASSWORD} + kakao: client_id: ${KAKAO_CLIENT_ID} @@ -54,3 +58,4 @@ haccp: default: image: https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Fbafebf27-4e21-4ed6-99e1-983eb90ad9c0%2F75c38d1f-215b-4536-a401-18cf179bf119%2Fimage.png?table=block&id=137a63f1-9420-8046-9cb6-d218151fd876&cache=v2 + From 7590486d304e921a4cf819d3fd1ace5210199c28 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Sun, 10 Nov 2024 06:09:21 +0900 Subject: [PATCH 27/38] feat: redis config --- .../jeje/work/aeatbe/config/RedisConfig.java | 36 ++++++++++++------- .../jeje/work/aeatbe/config/WebConfig.java | 2 +- src/main/resources/application.yml | 2 ++ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/config/RedisConfig.java b/src/main/java/jeje/work/aeatbe/config/RedisConfig.java index babd99a9..fcaa7382 100644 --- a/src/main/java/jeje/work/aeatbe/config/RedisConfig.java +++ b/src/main/java/jeje/work/aeatbe/config/RedisConfig.java @@ -4,6 +4,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; @@ -22,24 +23,33 @@ public class RedisConfig { @Value("${spring.data.redis.password}") private String password; + @Value("${spring.data.redis.username}") + private String username; + @Bean public RedisConnectionFactory redisConnectionFactory() { - return new LettuceConnectionFactory(host, port); + RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(host, port); + config.setUsername(username); + config.setPassword(password); + return new LettuceConnectionFactory(config); } - @Bean - public RedisTemplate redisTemplate() { - RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setConnectionFactory(redisConnectionFactory()); - - redisTemplate.setKeySerializer(new StringRedisSerializer()); - redisTemplate.setValueSerializer(new StringRedisSerializer()); - - redisTemplate.setDefaultSerializer(new StringRedisSerializer()); - - return redisTemplate; - } +// @Bean +// public RedisTemplate redisTemplate() { +// RedisTemplate redisTemplate = new RedisTemplate<>(); +// redisTemplate.setConnectionFactory(redisConnectionFactory()); +// +// redisTemplate.setKeySerializer(new StringRedisSerializer()); +// redisTemplate.setValueSerializer(new StringRedisSerializer()); +// +// redisTemplate.setHashKeySerializer(new StringRedisSerializer()); +// redisTemplate.setHashValueSerializer(new StringRedisSerializer()); +// +// redisTemplate.setDefaultSerializer(new StringRedisSerializer()); +// +// return redisTemplate; +// } diff --git a/src/main/java/jeje/work/aeatbe/config/WebConfig.java b/src/main/java/jeje/work/aeatbe/config/WebConfig.java index 3c19e0ba..e8563f0c 100644 --- a/src/main/java/jeje/work/aeatbe/config/WebConfig.java +++ b/src/main/java/jeje/work/aeatbe/config/WebConfig.java @@ -38,7 +38,7 @@ public void addArgumentResolvers(List argumentRes @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") - .allowedOriginPatterns("https://aeat.jeje.work", "http://localhost") + .allowedOriginPatterns("https://aeat.jeje.work", "http://localhost","*") .allowedMethods("*") .allowedHeaders("*") .allowCredentials(true) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 97c51b99..bc20a596 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -42,7 +42,9 @@ spring: redis: host: ${REDIS_HOST} port: ${REDIS_PORT} + url: ${REDIS_URL} password: ${REDIS_PASSWORD} + username: ${REDIS_ID} kakao: From 4e8205e2bb23f5fc7e7cfc36d4529290ac695865 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Sun, 10 Nov 2024 17:51:10 +0900 Subject: [PATCH 28/38] =?UTF-8?q?fix:=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jeje/work/aeatbe/entity/BlackList.java | 19 +++++++++++++++++++ .../aeatbe/interceptor/JwtInterceptor.java | 2 ++ 2 files changed, 21 insertions(+) create mode 100644 src/main/java/jeje/work/aeatbe/entity/BlackList.java diff --git a/src/main/java/jeje/work/aeatbe/entity/BlackList.java b/src/main/java/jeje/work/aeatbe/entity/BlackList.java new file mode 100644 index 00000000..8f56870a --- /dev/null +++ b/src/main/java/jeje/work/aeatbe/entity/BlackList.java @@ -0,0 +1,19 @@ +package jeje.work.aeatbe.entity; + +import jakarta.persistence.Id; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.data.redis.core.RedisHash; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@RedisHash("blackList") +public class BlackList { + + @Id + private String token; + + private String id; +} diff --git a/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java b/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java index 18924f3d..effcf110 100644 --- a/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java +++ b/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java @@ -38,6 +38,8 @@ public boolean preHandle(final HttpServletRequest request, final HttpServletResp if(!userService.validateToken(token)){ throw new TokenException("올바르지 않은 토큰입니다."); } + + return true; From a217d0c52ab9045443db4adf912c6e6597f462cf Mon Sep 17 00:00:00 2001 From: youcalste03 Date: Sun, 10 Nov 2024 19:41:19 +0900 Subject: [PATCH 29/38] feat:BlackListRepository --- .../jeje/work/aeatbe/repository/BlackListRepository.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java diff --git a/src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java b/src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java new file mode 100644 index 00000000..ab4e618a --- /dev/null +++ b/src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java @@ -0,0 +1,8 @@ +package jeje.work.aeatbe.repository; + +import jeje.work.aeatbe.entity.BlackList; +import org.springframework.data.repository.CrudRepository; + +public interface BlackListRepository extends CrudRepository { + +} From bd74d07c6b26a226903ceb020f47f46c96060d0d Mon Sep 17 00:00:00 2001 From: youcalste03 Date: Sun, 10 Nov 2024 20:43:31 +0900 Subject: [PATCH 30/38] feat:TokenService --- .../controller/KakaoAuthController.java | 5 +++-- .../jeje/work/aeatbe/entity/BlackList.java | 4 ++-- .../work/aeatbe/service/TokenService.java | 21 +++++++++++++++++++ src/main/resources/application.yml | 6 ++++-- 4 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 src/main/java/jeje/work/aeatbe/service/TokenService.java diff --git a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java index 8301101e..a5d4e1b2 100644 --- a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java +++ b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java @@ -16,6 +16,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -53,7 +54,7 @@ public ResponseEntity getAccessToken(@RequestParam String code) { KakaoTokenResponsed token = kakaoService.getKakaoTokenResponse(code); TokenResponseDTO tokenResponseDto = kakaoService.login(token.accessToken(), token.refreshToken()); HttpHeaders httpHeaders = userService.setCookie(tokenResponseDto); - return ResponseEntity.ok().headers(httpHeaders).build(); + return ResponseEntity.ok().headers(httpHeaders).body(tokenResponseDto); } /** @@ -63,7 +64,7 @@ public ResponseEntity getAccessToken(@RequestParam String code) { * @throws IOException */ @PostMapping("/logout") - public void logout(HttpServletResponse response, @LoginUser LoginUserInfo loginUserInfo) throws IOException{ + public void logout(HttpServletResponse response, @RequestHeader("Authorization") String token, @LoginUser LoginUserInfo loginUserInfo) throws IOException{ String url = kakaoProperties.logoutUrl() + "?client_id=" + kakaoProperties.clientId() + "&logout_redirect_uri=" + kakaoProperties.logoutRedirectUrl(); LogoutResponseDto logoutResponseDto = kakaoService.logout(loginUserInfo.userId()); diff --git a/src/main/java/jeje/work/aeatbe/entity/BlackList.java b/src/main/java/jeje/work/aeatbe/entity/BlackList.java index 8f56870a..3ac1a4eb 100644 --- a/src/main/java/jeje/work/aeatbe/entity/BlackList.java +++ b/src/main/java/jeje/work/aeatbe/entity/BlackList.java @@ -9,11 +9,11 @@ @Getter @NoArgsConstructor @AllArgsConstructor -@RedisHash("blackList") +@RedisHash(value = "blackList" , timeToLive = 3600) public class BlackList { @Id private String token; - private String id; + private Long id; } diff --git a/src/main/java/jeje/work/aeatbe/service/TokenService.java b/src/main/java/jeje/work/aeatbe/service/TokenService.java new file mode 100644 index 00000000..4f7cc50b --- /dev/null +++ b/src/main/java/jeje/work/aeatbe/service/TokenService.java @@ -0,0 +1,21 @@ +package jeje.work.aeatbe.service; + +import jeje.work.aeatbe.entity.BlackList; +import jeje.work.aeatbe.repository.BlackListRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class TokenService { + + private final BlackListRepository blackListRepository; + + + public void addBlackList (String accessToken, Long userId){ + BlackList blackList = new BlackList(accessToken, userId); + blackListRepository.save(blackList); + } + + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index bc20a596..f53a2247 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -49,10 +49,12 @@ spring: kakao: client_id: ${KAKAO_CLIENT_ID} - redirect_url: https://${FRONTEND_DOMAIN}/login/redirect +# redirect_url: https://${FRONTEND_DOMAIN}/login/redirect + redirect_url: http://localhost:8080/api/users/callback auth_url: https://kauth.kakao.com/oauth/authorize logout_url: https://kauth.kakao.com/oauth/logout - logout_redirect_url: https://${FRONTEND_DOMAIN}/logout/redirect +# logout_redirect_url: https://${FRONTEND_DOMAIN}/logout/redirect + logout_redirect_url: http://localhost:8080/api/users/logoutWithKakao/callback haccp: service_key : ${SERVICE_KEY} From 5b7670c7bdc8464d572074f5f7f5ac8242c4e7af Mon Sep 17 00:00:00 2001 From: youcalste03 Date: Sun, 10 Nov 2024 21:19:51 +0900 Subject: [PATCH 31/38] =?UTF-8?q?feat:=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83?= =?UTF-8?q?=EC=8B=9C=20=EB=B8=94=EB=9E=99=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jeje/work/aeatbe/config/RedisConfig.java | 30 +++++++++---------- .../controller/KakaoAuthController.java | 3 ++ .../jeje/work/aeatbe/entity/BlackList.java | 3 +- .../work/aeatbe/service/TokenService.java | 18 ++++++++++- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/config/RedisConfig.java b/src/main/java/jeje/work/aeatbe/config/RedisConfig.java index fcaa7382..872dae17 100644 --- a/src/main/java/jeje/work/aeatbe/config/RedisConfig.java +++ b/src/main/java/jeje/work/aeatbe/config/RedisConfig.java @@ -35,21 +35,21 @@ public RedisConnectionFactory redisConnectionFactory() { } -// @Bean -// public RedisTemplate redisTemplate() { -// RedisTemplate redisTemplate = new RedisTemplate<>(); -// redisTemplate.setConnectionFactory(redisConnectionFactory()); -// -// redisTemplate.setKeySerializer(new StringRedisSerializer()); -// redisTemplate.setValueSerializer(new StringRedisSerializer()); -// -// redisTemplate.setHashKeySerializer(new StringRedisSerializer()); -// redisTemplate.setHashValueSerializer(new StringRedisSerializer()); -// -// redisTemplate.setDefaultSerializer(new StringRedisSerializer()); -// -// return redisTemplate; -// } + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory()); + + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(new StringRedisSerializer()); + + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashValueSerializer(new StringRedisSerializer()); + + redisTemplate.setDefaultSerializer(new StringRedisSerializer()); + + return redisTemplate; + } diff --git a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java index a5d4e1b2..8830f6a2 100644 --- a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java +++ b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java @@ -9,6 +9,7 @@ import jeje.work.aeatbe.dto.user.TokenResponseDTO; import jeje.work.aeatbe.dto.user.LoginUserInfo; import jeje.work.aeatbe.service.KakaoService; +import jeje.work.aeatbe.service.TokenService; import jeje.work.aeatbe.service.UserService; import jeje.work.aeatbe.utility.JwtUtil; import lombok.RequiredArgsConstructor; @@ -30,6 +31,7 @@ public class KakaoAuthController { private final JwtUtil jwtUtil; private final KakaoProperties kakaoProperties; private final KakaoService kakaoService; + private final TokenService tokenService; /** * 카카오 로그인페이지로 리다이렉션 @@ -67,6 +69,7 @@ public ResponseEntity getAccessToken(@RequestParam String code) { public void logout(HttpServletResponse response, @RequestHeader("Authorization") String token, @LoginUser LoginUserInfo loginUserInfo) throws IOException{ String url = kakaoProperties.logoutUrl() + "?client_id=" + kakaoProperties.clientId() + "&logout_redirect_uri=" + kakaoProperties.logoutRedirectUrl(); + tokenService.addBlackList(token, loginUserInfo.userId()); LogoutResponseDto logoutResponseDto = kakaoService.logout(loginUserInfo.userId()); response.sendRedirect(url); } diff --git a/src/main/java/jeje/work/aeatbe/entity/BlackList.java b/src/main/java/jeje/work/aeatbe/entity/BlackList.java index 3ac1a4eb..d306b52d 100644 --- a/src/main/java/jeje/work/aeatbe/entity/BlackList.java +++ b/src/main/java/jeje/work/aeatbe/entity/BlackList.java @@ -1,9 +1,10 @@ package jeje.work.aeatbe.entity; -import jakarta.persistence.Id; + import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; +import org.springframework.data.annotation.Id; import org.springframework.data.redis.core.RedisHash; @Getter diff --git a/src/main/java/jeje/work/aeatbe/service/TokenService.java b/src/main/java/jeje/work/aeatbe/service/TokenService.java index 4f7cc50b..3297aafe 100644 --- a/src/main/java/jeje/work/aeatbe/service/TokenService.java +++ b/src/main/java/jeje/work/aeatbe/service/TokenService.java @@ -11,11 +11,27 @@ public class TokenService { private final BlackListRepository blackListRepository; - + /** + * 로그아웃된 엑세스 토큰을 블랙 리스트에 등록 + * @param accessToken + * @param userId + */ public void addBlackList (String accessToken, Long userId){ BlackList blackList = new BlackList(accessToken, userId); blackListRepository.save(blackList); } + /** + * 토큰의 Bearer prefix 제거 + * @param token + * @return prefix없는 순수 토큰 + */ + public String removePrefix(String token){ + if(token.startsWith("Bearer ")){ + return token.substring(7); + } + return token; + } + } From ac02e1a620ee5dbe21a49f96cc0a031a948f7055 Mon Sep 17 00:00:00 2001 From: youcalste03 Date: Sun, 10 Nov 2024 21:37:57 +0900 Subject: [PATCH 32/38] =?UTF-8?q?refactor:=20cookie=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=20=EC=B7=A8=EC=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jeje/work/aeatbe/controller/KakaoAuthController.java | 6 +++--- .../java/jeje/work/aeatbe/controller/TokenController.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java index 8830f6a2..65900a22 100644 --- a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java +++ b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java @@ -52,11 +52,11 @@ public void redirectKakaoLogin(HttpServletResponse response) throws IOException * @return httpHeader(Cookie) */ @GetMapping("/callback") - public ResponseEntity getAccessToken(@RequestParam String code) { + public ResponseEntity getAccessToken(@RequestParam String code) { KakaoTokenResponsed token = kakaoService.getKakaoTokenResponse(code); TokenResponseDTO tokenResponseDto = kakaoService.login(token.accessToken(), token.refreshToken()); HttpHeaders httpHeaders = userService.setCookie(tokenResponseDto); - return ResponseEntity.ok().headers(httpHeaders).body(tokenResponseDto); + return ResponseEntity.ok().body(tokenResponseDto); } /** @@ -69,7 +69,7 @@ public ResponseEntity getAccessToken(@RequestParam String code) { public void logout(HttpServletResponse response, @RequestHeader("Authorization") String token, @LoginUser LoginUserInfo loginUserInfo) throws IOException{ String url = kakaoProperties.logoutUrl() + "?client_id=" + kakaoProperties.clientId() + "&logout_redirect_uri=" + kakaoProperties.logoutRedirectUrl(); - tokenService.addBlackList(token, loginUserInfo.userId()); + tokenService.addBlackList(tokenService.removePrefix(token), loginUserInfo.userId()); LogoutResponseDto logoutResponseDto = kakaoService.logout(loginUserInfo.userId()); response.sendRedirect(url); } diff --git a/src/main/java/jeje/work/aeatbe/controller/TokenController.java b/src/main/java/jeje/work/aeatbe/controller/TokenController.java index 45ecb88d..7ddc3e3f 100644 --- a/src/main/java/jeje/work/aeatbe/controller/TokenController.java +++ b/src/main/java/jeje/work/aeatbe/controller/TokenController.java @@ -27,6 +27,6 @@ public class TokenController { public ResponseEntity refreshToken(@RequestBody RefreshTokenRequestDTO refreshTokenRequestDTO){ TokenResponseDTO tokenResponseDto = userService.reissueAccessToken(refreshTokenRequestDTO.refreshToken()); HttpHeaders httpHeaders = userService.setCookie(tokenResponseDto); - return ResponseEntity.ok().headers(httpHeaders).build(); + return ResponseEntity.ok().body(tokenResponseDto); } } From 1f526f6f526475f161d7fd9b26d4a5cf60be0d8b Mon Sep 17 00:00:00 2001 From: youcalste03 Date: Sun, 10 Nov 2024 23:52:58 +0900 Subject: [PATCH 33/38] feat: blackList --- .../java/jeje/work/aeatbe/config/WebConfig.java | 3 ++- .../aeatbe/controller/KakaoAuthController.java | 2 +- .../java/jeje/work/aeatbe/entity/BlackList.java | 2 +- .../work/aeatbe/interceptor/JwtInterceptor.java | 5 +++++ .../aeatbe/repository/BlackListRepository.java | 1 + .../jeje/work/aeatbe/service/TokenService.java | 17 ++++++++++++++++- 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/config/WebConfig.java b/src/main/java/jeje/work/aeatbe/config/WebConfig.java index e8563f0c..780e8f35 100644 --- a/src/main/java/jeje/work/aeatbe/config/WebConfig.java +++ b/src/main/java/jeje/work/aeatbe/config/WebConfig.java @@ -27,7 +27,8 @@ public void addInterceptors(InterceptorRegistry registry) { .addPathPatterns("/api/article/likes/**") .addPathPatterns("/api/users/logout/**") . addPathPatterns("/api/wishlist/**") - .addPathPatterns("/api/reviews/my/**"); + .addPathPatterns("/api/reviews/my/**") + .addPathPatterns("/api/users/info/**"); } @Override diff --git a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java index 65900a22..d7fedd7b 100644 --- a/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java +++ b/src/main/java/jeje/work/aeatbe/controller/KakaoAuthController.java @@ -69,7 +69,7 @@ public ResponseEntity getAccessToken(@RequestParam String code public void logout(HttpServletResponse response, @RequestHeader("Authorization") String token, @LoginUser LoginUserInfo loginUserInfo) throws IOException{ String url = kakaoProperties.logoutUrl() + "?client_id=" + kakaoProperties.clientId() + "&logout_redirect_uri=" + kakaoProperties.logoutRedirectUrl(); - tokenService.addBlackList(tokenService.removePrefix(token), loginUserInfo.userId()); + tokenService.addBlackList(tokenService.removePrefix(token), loginUserInfo.userId().toString()); LogoutResponseDto logoutResponseDto = kakaoService.logout(loginUserInfo.userId()); response.sendRedirect(url); } diff --git a/src/main/java/jeje/work/aeatbe/entity/BlackList.java b/src/main/java/jeje/work/aeatbe/entity/BlackList.java index d306b52d..a1b1c4ac 100644 --- a/src/main/java/jeje/work/aeatbe/entity/BlackList.java +++ b/src/main/java/jeje/work/aeatbe/entity/BlackList.java @@ -16,5 +16,5 @@ public class BlackList { @Id private String token; - private Long id; + private String userId; } diff --git a/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java b/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java index effcf110..16075ecb 100644 --- a/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java +++ b/src/main/java/jeje/work/aeatbe/interceptor/JwtInterceptor.java @@ -4,6 +4,7 @@ import jakarta.servlet.http.HttpServletResponse; import jeje.work.aeatbe.exception.TokenExpException; import jeje.work.aeatbe.exception.TokenException; +import jeje.work.aeatbe.service.TokenService; import jeje.work.aeatbe.service.UserService; import jeje.work.aeatbe.utility.JwtUtil; import lombok.AllArgsConstructor; @@ -17,6 +18,7 @@ public class JwtInterceptor implements HandlerInterceptor { private JwtUtil jwtUtil; private UserService userService; + private TokenService tokenService; @Override public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) @@ -29,6 +31,9 @@ public boolean preHandle(final HttpServletRequest request, final HttpServletResp String token = header.substring(7); + if(tokenService.isInBlackList(token)){ + throw new TokenException("이미로그아웃 된 사용자의 토큰입니다. 다시 로그인 해주세요"); + } if(jwtUtil.validTokenExpiration(token, true)) { throw new TokenExpException("만료된 토큰입니다."); diff --git a/src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java b/src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java index ab4e618a..d916ca8d 100644 --- a/src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java +++ b/src/main/java/jeje/work/aeatbe/repository/BlackListRepository.java @@ -1,5 +1,6 @@ package jeje.work.aeatbe.repository; +import java.util.Optional; import jeje.work.aeatbe.entity.BlackList; import org.springframework.data.repository.CrudRepository; diff --git a/src/main/java/jeje/work/aeatbe/service/TokenService.java b/src/main/java/jeje/work/aeatbe/service/TokenService.java index 3297aafe..4d89b5df 100644 --- a/src/main/java/jeje/work/aeatbe/service/TokenService.java +++ b/src/main/java/jeje/work/aeatbe/service/TokenService.java @@ -1,8 +1,10 @@ package jeje.work.aeatbe.service; +import java.util.Optional; import jeje.work.aeatbe.entity.BlackList; import jeje.work.aeatbe.repository.BlackListRepository; import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @Service @@ -10,13 +12,15 @@ public class TokenService { private final BlackListRepository blackListRepository; + private final RedisTemplate redisTemplate; /** * 로그아웃된 엑세스 토큰을 블랙 리스트에 등록 * @param accessToken * @param userId */ - public void addBlackList (String accessToken, Long userId){ + public void addBlackList (String accessToken, String userId){ + accessToken = removePrefix(accessToken); BlackList blackList = new BlackList(accessToken, userId); blackListRepository.save(blackList); } @@ -33,5 +37,16 @@ public String removePrefix(String token){ return token; } + /** + * 블랙시스트에 있는지 확인 + * @param accessToken + * @return 유무 + */ + public boolean isInBlackList(String accessToken){ + Optional blackList = blackListRepository.findById(accessToken); + + return !blackList.isEmpty(); + } + } From ead10d910104f5a4f51fa6d30ac87ddf7ba72c11 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Mon, 11 Nov 2024 01:30:31 +0900 Subject: [PATCH 34/38] =?UTF-8?q?chore:=20=EB=B9=88=EC=B9=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/jeje/work/aeatbe/config/RedisConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/jeje/work/aeatbe/config/RedisConfig.java b/src/main/java/jeje/work/aeatbe/config/RedisConfig.java index 872dae17..82f70f86 100644 --- a/src/main/java/jeje/work/aeatbe/config/RedisConfig.java +++ b/src/main/java/jeje/work/aeatbe/config/RedisConfig.java @@ -35,6 +35,7 @@ public RedisConnectionFactory redisConnectionFactory() { } + @Bean public RedisTemplate redisTemplate() { RedisTemplate redisTemplate = new RedisTemplate<>(); From 7823f9fc82a00fea6e50489268e2760906d98b74 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Mon, 11 Nov 2024 11:39:04 +0900 Subject: [PATCH 35/38] =?UTF-8?q?fix:=20test=EC=9A=A9=20url=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/spring_test.yml | 3 +++ src/main/resources/application.yml | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/spring_test.yml b/.github/workflows/spring_test.yml index 57a4ee1d..3b7da5d7 100644 --- a/.github/workflows/spring_test.yml +++ b/.github/workflows/spring_test.yml @@ -28,6 +28,9 @@ jobs: DB_CONTAINER_NAME=super_poo_container JWT_SECRET_KEY=top_secret_key_for_ci SERVICE_KEY = test_key + REDIS_PORT = 1111 + REDIS_URL = jdbc:redis://aaa/superfoo:1111 + EOF - name: Set up JDK 21 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f53a2247..7f20e765 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -49,11 +49,9 @@ spring: kakao: client_id: ${KAKAO_CLIENT_ID} -# redirect_url: https://${FRONTEND_DOMAIN}/login/redirect redirect_url: http://localhost:8080/api/users/callback auth_url: https://kauth.kakao.com/oauth/authorize logout_url: https://kauth.kakao.com/oauth/logout -# logout_redirect_url: https://${FRONTEND_DOMAIN}/logout/redirect logout_redirect_url: http://localhost:8080/api/users/logoutWithKakao/callback haccp: From c89b328eb2745812822cbea84d1aa4e178959444 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Mon, 11 Nov 2024 11:40:07 +0900 Subject: [PATCH 36/38] =?UTF-8?q?fix:=20url=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/jeje/work/aeatbe/service/UserService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/jeje/work/aeatbe/service/UserService.java b/src/main/java/jeje/work/aeatbe/service/UserService.java index 72ee9e80..520a9ead 100644 --- a/src/main/java/jeje/work/aeatbe/service/UserService.java +++ b/src/main/java/jeje/work/aeatbe/service/UserService.java @@ -162,7 +162,7 @@ public HttpHeaders setCookie(TokenResponseDTO tokenResponseDTO){ .secure(true) .path("/") .maxAge(3600*24*14) - .domain(".aeat.jeje.work") + .domain(".jeje.work") .sameSite("None") .build(); ResponseCookie accessCookie = ResponseCookie.from("Authorization-accessToken", tokenResponseDTO.accessToken()) @@ -170,7 +170,7 @@ public HttpHeaders setCookie(TokenResponseDTO tokenResponseDTO){ .secure(true) .path("/") .maxAge(3600) - .domain(".aeat.jeje.work") + .domain(".jeje.work") .sameSite("None") .build(); HttpHeaders headers = new HttpHeaders(); From b340dd2dc7455f8f1ae25e254d678bb0ca0ad929 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Mon, 11 Nov 2024 11:52:19 +0900 Subject: [PATCH 37/38] =?UTF-8?q?feat:testyml=EC=97=90=20redis=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/resources/application.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index fb508d88..226a8dfd 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -22,6 +22,14 @@ spring: sql: init: mode: never + data: + redis: + host: ${REDIS_HOST} + port: ${REDIS_PORT} + url: ${REDIS_URL} + password: ${REDIS_PASSWORD} + username: ${REDIS_ID} + kakao: client_id: ${KAKAO_CLIENT_ID} From d4717e2071dff65b8049e82415d1e58ac52575a4 Mon Sep 17 00:00:00 2001 From: youcaslte03 Date: Mon, 11 Nov 2024 11:57:40 +0900 Subject: [PATCH 38/38] =?UTF-8?q?fix:test=20yml=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/spring_test.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/spring_test.yml b/.github/workflows/spring_test.yml index 3b7da5d7..0d6facb2 100644 --- a/.github/workflows/spring_test.yml +++ b/.github/workflows/spring_test.yml @@ -30,6 +30,9 @@ jobs: SERVICE_KEY = test_key REDIS_PORT = 1111 REDIS_URL = jdbc:redis://aaa/superfoo:1111 + REDIS_HOST = yeah + REDIS_ID = hello + REDIS_PASSWORD = redis EOF