diff --git a/src/main/java/com/api/farmingsoon/common/exception/ErrorCode.java b/src/main/java/com/api/farmingsoon/common/exception/ErrorCode.java index 1a1375f..4c04bc1 100644 --- a/src/main/java/com/api/farmingsoon/common/exception/ErrorCode.java +++ b/src/main/java/com/api/farmingsoon/common/exception/ErrorCode.java @@ -32,6 +32,7 @@ public enum ErrorCode { FORBIDDEN_DELETE("삭제 권한이 없습니다.", HttpStatus.FORBIDDEN), FORBIDDEN_UPDATE("수정 권한이 없습니다.", HttpStatus.FORBIDDEN), EMPTY_AUTHORITY("권한 정보가 비어있습니다.", HttpStatus.FORBIDDEN), + OWN_ITEM("자신의 게시글에는 좋아요를 누를 수 없습니다.", HttpStatus.FORBIDDEN), // 404 NOT_FOUND_MEMBER("회원이 존재하지 않습니다.", HttpStatus.NOT_FOUND), diff --git a/src/main/java/com/api/farmingsoon/common/interceptor/AuthenticationInterceptor.java b/src/main/java/com/api/farmingsoon/common/interceptor/AuthenticationInterceptor.java index cbfc996..318d449 100644 --- a/src/main/java/com/api/farmingsoon/common/interceptor/AuthenticationInterceptor.java +++ b/src/main/java/com/api/farmingsoon/common/interceptor/AuthenticationInterceptor.java @@ -26,10 +26,8 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons log.info(request.getHeader("Origin")); log.info("Authentication Interceptor : " + request.getRequestURI()); String accessToken = CookieUtils.getAccessTokenCookieValue(request); - String refreshToken = CookieUtils.getRefreshTokenCookieValue(request); log.info(accessToken); - log.info(refreshToken); if (accessToken != null) { // 토큰 재발급의 요청이 아니면서 accessToken이 존재할 때 if(jwtProvider.validateAccessToken(accessToken)){ diff --git a/src/main/java/com/api/farmingsoon/common/interceptor/WebSocketHandshakeInterceptor.java b/src/main/java/com/api/farmingsoon/common/interceptor/WebSocketHandshakeInterceptor.java index f71182b..18fde39 100644 --- a/src/main/java/com/api/farmingsoon/common/interceptor/WebSocketHandshakeInterceptor.java +++ b/src/main/java/com/api/farmingsoon/common/interceptor/WebSocketHandshakeInterceptor.java @@ -1,11 +1,18 @@ package com.api.farmingsoon.common.interceptor; +import com.api.farmingsoon.common.exception.CustomException; +import com.api.farmingsoon.common.exception.ErrorCode; +import com.api.farmingsoon.common.exception.custom_exception.ForbiddenException; +import com.api.farmingsoon.common.security.jwt.JwtProvider; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.HandshakeInterceptor; @@ -14,18 +21,26 @@ import java.util.Map; @Component +@RequiredArgsConstructor @Slf4j public class WebSocketHandshakeInterceptor implements HandshakeInterceptor { + private final JwtProvider jwtProvider; @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception { + log.info("beforeHandshake"); if (request instanceof ServletServerHttpRequest) { ServletServerHttpRequest servletServerRequest = (ServletServerHttpRequest) request; HttpServletRequest servletRequest = servletServerRequest.getServletRequest(); // 쿠키 정보 전달 - Cookie token = WebUtils.getCookie(servletRequest, "RefreshToken"); - log.info(String.valueOf(token)); + String accessToken = WebUtils.getCookie(servletRequest, "AccessToken").getValue(); + log.info(accessToken); + + if (accessToken == null) + throw new ForbiddenException(ErrorCode.NOT_LOGIN); + else + jwtProvider.validateAccessToken(accessToken); } - log.info("beforeHandshake"); + return true; } diff --git a/src/main/java/com/api/farmingsoon/domain/item/dto/ItemDetailResponse.java b/src/main/java/com/api/farmingsoon/domain/item/dto/ItemDetailResponse.java index ae44563..db9988f 100644 --- a/src/main/java/com/api/farmingsoon/domain/item/dto/ItemDetailResponse.java +++ b/src/main/java/com/api/farmingsoon/domain/item/dto/ItemDetailResponse.java @@ -57,7 +57,7 @@ public static ItemDetailResponse of(Item item, Optional viewer) { .likeStatus // 조회자 세션이 존재할 경우에만 비교를 한다. ( viewer.isPresent() ? - item.getLikeableItemList().stream().map(LikeableItem::getMember).toList().contains(viewer) : false + item.getLikeableItemList().stream().map(LikeableItem::getMember).toList().contains(viewer.get()) : false ) .build(); } diff --git a/src/main/java/com/api/farmingsoon/domain/item/dto/ItemListResponse.java b/src/main/java/com/api/farmingsoon/domain/item/dto/ItemListResponse.java index 5a21a97..a255462 100644 --- a/src/main/java/com/api/farmingsoon/domain/item/dto/ItemListResponse.java +++ b/src/main/java/com/api/farmingsoon/domain/item/dto/ItemListResponse.java @@ -68,7 +68,6 @@ private ItemResponse(Long itemId, String title, String description, LocalDateTim } private static ItemResponse of(Item item, Optional viewer) { - return ItemResponse.builder() .itemId(item.getId()) .title(item.getTitle()) @@ -84,8 +83,7 @@ private static ItemResponse of(Item item, Optional viewer) { .thumbnailImgUrl(item.getThumbnailImageUrl()) .likeStatus // 조회자 세션이 존재할 경우에만 비교를 한다. ( - viewer.isPresent() ? - item.getLikeableItemList().stream().map(LikeableItem::getMember).toList().contains(viewer) : false + viewer.isPresent() ? item.getLikeableItemList().stream().map(LikeableItem::getMember).toList().contains(viewer.get()) : false ) .build(); } diff --git a/src/main/java/com/api/farmingsoon/domain/like/service/LikeableItemService.java b/src/main/java/com/api/farmingsoon/domain/like/service/LikeableItemService.java index a03cb48..4ea2918 100644 --- a/src/main/java/com/api/farmingsoon/domain/like/service/LikeableItemService.java +++ b/src/main/java/com/api/farmingsoon/domain/like/service/LikeableItemService.java @@ -2,6 +2,7 @@ import com.api.farmingsoon.common.exception.ErrorCode; import com.api.farmingsoon.common.exception.custom_exception.DuplicateException; +import com.api.farmingsoon.common.exception.custom_exception.ForbiddenException; import com.api.farmingsoon.common.exception.custom_exception.NotFoundException; import com.api.farmingsoon.common.util.AuthenticationUtils; import com.api.farmingsoon.domain.item.domain.Item; @@ -33,6 +34,9 @@ public void like(Long itemId) { Member member = authenticationUtils.getAuthenticationMember(); Item item = itemRepository.findById(itemId).orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_ITEM)); + if(item.getMember() == member) + throw new ForbiddenException(ErrorCode.OWN_ITEM); + // 해당 상품에 이미 좋아요를 누른 경우 예외 처리 likeableItemRepository.findByMemberAndItem(member, item).ifPresent(it -> { throw new DuplicateException(ErrorCode.ALREADY_LIKED); diff --git a/src/test/java/com/api/farmingsoon/domain/like/LikeableItemIntegrationTest.java b/src/test/java/com/api/farmingsoon/domain/like/LikeableItemIntegrationTest.java index d38ca45..59372bd 100644 --- a/src/test/java/com/api/farmingsoon/domain/like/LikeableItemIntegrationTest.java +++ b/src/test/java/com/api/farmingsoon/domain/like/LikeableItemIntegrationTest.java @@ -6,6 +6,7 @@ import com.api.farmingsoon.domain.IntegrationTest; import com.api.farmingsoon.domain.item.domain.Item; import com.api.farmingsoon.domain.item.domain.ItemStatus; +import com.api.farmingsoon.domain.item.dto.ItemListResponse; import com.api.farmingsoon.domain.item.dto.LikeableItemListResponse; import com.api.farmingsoon.domain.item.service.ItemService; import com.api.farmingsoon.domain.like.service.LikeableItemService; @@ -75,14 +76,14 @@ static void beforeAll() throws IOException { @BeforeEach void beforeEach(){ databaseCleanup.execute(); - JoinRequest joinRequest = JoinRequest.builder() - .email("user1@naver.com") - .nickname("user1") - .password("12345678") - .profileImg(thumbnailImage).build(); - - memberService.join(joinRequest); - + for(int i = 1; i <= 2; i++){ + JoinRequest joinRequest = JoinRequest.builder() + .email("user" + i +"@naver.com") + .nickname("user" + i) + .password("12345678") + .profileImg(thumbnailImage).build(); + memberService.join(joinRequest); + } Collection authorities = List.of(new SimpleGrantedAuthority("ROLE_MEMBER")); UserDetails principal = new User("user1@naver.com", "", authorities); @@ -98,12 +99,11 @@ void beforeEach(){ .viewCount(i) .expiredAt(TimeUtils.setExpireAt(i)).build(); + List imageUrl = new ArrayList<>(Arrays.asList("/subFile1/" + i, "/subFile2/" + i, "/subFile3/" + i)); imageUrl.add(0, "/thumnailImage/" + i); - Long itemId = itemService.saveItemAndImage(item, imageUrl); - if(itemId % 2 == 0) // 짝수 상품에만 좋아요 등록 - likeableItemService.like(itemId); + itemService.saveItemAndImage(item, imageUrl); } this.mockMvc = MockMvcBuilders.webAppContextSetup(ctx) @@ -118,7 +118,7 @@ void beforeEach(){ * 조회 */ @DisplayName("관심 상품 등록 성공") - @WithUserDetails(value = "user1@naver.com", setupBefore = TestExecutionEvent.TEST_EXECUTION) + @WithUserDetails(value = "user2@naver.com", setupBefore = TestExecutionEvent.TEST_EXECUTION) @Test void likeableItemCreateSuccess() throws Exception { // when @@ -136,12 +136,33 @@ void likeableItemCreateSuccess() throws Exception { } ); } - @DisplayName("관심 상품 삭제 성공") + @DisplayName("자신의 상품에는 좋아요 등록 불가") @WithUserDetails(value = "user1@naver.com", setupBefore = TestExecutionEvent.TEST_EXECUTION) @Test + void likeableItemCreateFail() throws Exception { + // when + MvcResult mvcResult = mockMvc.perform(post("/api/likeable-items/" + 1)) + .andDo(print()) + .andExpect(status().isForbidden()) + .andReturn(); + //then + transaction.invoke(() -> { + Item item = itemService.getItemById(1L); + Assertions.assertThat(item.getLikeableItemList().size()).isEqualTo(0); + return item.getId(); + } + ); + } + + @DisplayName("관심 상품 삭제 성공") + @WithUserDetails(value = "user2@naver.com", setupBefore = TestExecutionEvent.TEST_EXECUTION) + @Test void likeableItemDeleteSuccess() throws Exception { // when - MvcResult mvcResult = mockMvc.perform(delete("/api/likeable-items/" + 2)) + likeableItemService.like((long) 1); + + + MvcResult mvcResult = mockMvc.perform(delete("/api/likeable-items/" + 1)) .andDo(print()) .andExpect(status().isOk()) .andReturn(); @@ -155,11 +176,18 @@ void likeableItemDeleteSuccess() throws Exception { } ); } + + @DisplayName("관심 상품 조회 성공") - @WithUserDetails(value = "user1@naver.com", setupBefore = TestExecutionEvent.TEST_EXECUTION) + @WithUserDetails(value = "user2@naver.com", setupBefore = TestExecutionEvent.TEST_EXECUTION) @Test void getLikeableItemSuccess() throws Exception { // when + for(int itemId = 1; itemId <= 20; itemId++) { + if (itemId % 2 == 0) // 짝수 상품에만 좋아요 등록 + likeableItemService.like((long) itemId); + } + MvcResult mvcResult = mockMvc.perform(get("/api/likeable-items/me")) .andDo(print()) .andExpect(status().isOk()) @@ -170,4 +198,26 @@ void getLikeableItemSuccess() throws Exception { Assertions.assertThat(likeableItemListResponse.getItems().size()).isEqualTo(10); } + + @DisplayName("상품 조회시 관심상품 likeStatus는 true") + @WithUserDetails(value = "user2@naver.com", setupBefore = TestExecutionEvent.TEST_EXECUTION) + @Test + void checkItemLikeStatus() throws Exception { + // when + for(int itemId = 1; itemId <= 20; itemId++) { + if (itemId % 2 == 0) // 짝수 상품에만 좋아요 등록 + likeableItemService.like((long) itemId); + } + + MvcResult mvcResult = mockMvc.perform(get("/api/items")) + .andDo(print()) + .andExpect(status().isOk()) + .andReturn(); + //then + String result = objectMapper.readTree(mvcResult.getResponse().getContentAsString()).get("result").toString(); + ItemListResponse itemListResponse = objectMapper.readValue(result, ItemListResponse.class); + + Assertions.assertThat(itemListResponse.getItems().get(0).getLikeStatus()).isEqualTo(true); + Assertions.assertThat(itemListResponse.getItems().get(1).getLikeStatus()).isEqualTo(false); + } } \ No newline at end of file