From 733796b3f547d32a02ebec3f46389adf998e6b87 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Sun, 2 Mar 2025 15:33:59 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../swyp8team2/auth/application/AuthService.java | 5 +++++ .../auth/application/jwt/JwtService.java | 6 ++++++ .../auth/domain/RefreshTokenRepository.java | 2 ++ .../auth/presentation/AuthController.java | 13 +++++++++++++ .../RefreshTokenCookieGenerator.java | 16 ++++++++++++++++ 5 files changed, 42 insertions(+) diff --git a/src/main/java/com/swyp8team2/auth/application/AuthService.java b/src/main/java/com/swyp8team2/auth/application/AuthService.java index d19a6203..17afa7d2 100644 --- a/src/main/java/com/swyp8team2/auth/application/AuthService.java +++ b/src/main/java/com/swyp8team2/auth/application/AuthService.java @@ -61,4 +61,9 @@ public String createGuestToken() { Long guestId = userService.createGuest(); return cryptoService.encrypt(String.valueOf(guestId)); } + + @Transactional + public void signOut(String refreshToken) { + jwtService.signOut(refreshToken); + } } diff --git a/src/main/java/com/swyp8team2/auth/application/jwt/JwtService.java b/src/main/java/com/swyp8team2/auth/application/jwt/JwtService.java index b7963ca8..14a4e5df 100644 --- a/src/main/java/com/swyp8team2/auth/application/jwt/JwtService.java +++ b/src/main/java/com/swyp8team2/auth/application/jwt/JwtService.java @@ -44,4 +44,10 @@ public TokenResponse reissue(String refreshToken) { claim.id(), tokenPair.accessToken(), tokenPair.refreshToken()); return new TokenResponse(tokenPair, claim.idAsLong()); } + + @Transactional + public void signOut(String refreshToken) { + JwtClaim claim = jwtProvider.parseToken(refreshToken); + refreshTokenRepository.deleteByUserId(claim.idAsLong()); + } } diff --git a/src/main/java/com/swyp8team2/auth/domain/RefreshTokenRepository.java b/src/main/java/com/swyp8team2/auth/domain/RefreshTokenRepository.java index b406ecfa..625a1c74 100644 --- a/src/main/java/com/swyp8team2/auth/domain/RefreshTokenRepository.java +++ b/src/main/java/com/swyp8team2/auth/domain/RefreshTokenRepository.java @@ -6,4 +6,6 @@ public interface RefreshTokenRepository extends JpaRepository { Optional findByUserId(Long userId); + + void deleteByUserId(Long userId); } diff --git a/src/main/java/com/swyp8team2/auth/presentation/AuthController.java b/src/main/java/com/swyp8team2/auth/presentation/AuthController.java index 249f2a7d..bb0a0f70 100644 --- a/src/main/java/com/swyp8team2/auth/presentation/AuthController.java +++ b/src/main/java/com/swyp8team2/auth/presentation/AuthController.java @@ -63,4 +63,17 @@ public ResponseEntity guestToken() { String guestToken = authService.createGuestToken(); return ResponseEntity.ok(new GuestTokenResponse(guestToken)); } + + @PostMapping("/sign-out") + public ResponseEntity signOut( + @CookieValue(name = CustomHeader.CustomCookie.REFRESH_TOKEN, required = false) String refreshToken, + HttpServletResponse response + ) { + if (Objects.isNull(refreshToken)) { + throw new BadRequestException(ErrorCode.INVALID_REFRESH_TOKEN_HEADER); + } + refreshTokenCookieGenerator.removeCookie(response); + authService.signOut(refreshToken); + return ResponseEntity.ok().build(); + } } diff --git a/src/main/java/com/swyp8team2/auth/presentation/RefreshTokenCookieGenerator.java b/src/main/java/com/swyp8team2/auth/presentation/RefreshTokenCookieGenerator.java index 8e0c50f8..825df15d 100644 --- a/src/main/java/com/swyp8team2/auth/presentation/RefreshTokenCookieGenerator.java +++ b/src/main/java/com/swyp8team2/auth/presentation/RefreshTokenCookieGenerator.java @@ -2,6 +2,7 @@ import com.swyp8team2.common.presentation.CustomHeader; import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -24,4 +25,19 @@ public Cookie createCookie(String refreshToken) { cookie.setMaxAge(60 * 60 * 24 * 14); return cookie; } + + public void removeCookie(HttpServletResponse response) { + Cookie cookie = new Cookie(CustomHeader.CustomCookie.REFRESH_TOKEN, null); + cookie.setHttpOnly(true); + cookie.setSecure(true); + if ("local".equals(activeProfile)) { + cookie.setSecure(false); + } else { + cookie.setSecure(true); + cookie.setAttribute("SameSite", "None"); + } + cookie.setPath("/"); + cookie.setMaxAge(0); + response.addCookie(cookie); + } } From 2dd28b7707c4987374dd33f94e4c974ad3c1e88b Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Sun, 2 Mar 2025 15:34:11 +0900 Subject: [PATCH 2/6] =?UTF-8?q?test:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/application/JwtServiceTest.java | 18 +++++++++++++ .../auth/presentation/AuthControllerTest.java | 26 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/test/java/com/swyp8team2/auth/application/JwtServiceTest.java b/src/test/java/com/swyp8team2/auth/application/JwtServiceTest.java index bd08fc2f..c39f8035 100644 --- a/src/test/java/com/swyp8team2/auth/application/JwtServiceTest.java +++ b/src/test/java/com/swyp8team2/auth/application/JwtServiceTest.java @@ -17,6 +17,7 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; class JwtServiceTest extends IntegrationTest { @@ -103,4 +104,21 @@ void reissue_refreshTokenMismatched() throws Exception { .isInstanceOf(BadRequestException.class) .hasMessage(ErrorCode.REFRESH_TOKEN_MISMATCHED.getMessage()); } + + @Test + @DisplayName("로그아웃하면 refresh token을 db에서 삭제해야 함") + void signOut() throws Exception { + //given + long givenUserId = 1L; + String givenRefreshToken = "refreshToken"; + given(jwtProvider.parseToken(eq(givenRefreshToken))) + .willReturn(new JwtClaim(givenUserId)); + refreshTokenRepository.save(new RefreshToken(givenUserId, givenRefreshToken)); + + //when + jwtService.signOut(givenRefreshToken); + + //then + assertThat(refreshTokenRepository.findByUserId(givenUserId)).isEmpty(); + } } diff --git a/src/test/java/com/swyp8team2/auth/presentation/AuthControllerTest.java b/src/test/java/com/swyp8team2/auth/presentation/AuthControllerTest.java index a9a9aa4f..6b4c6a51 100644 --- a/src/test/java/com/swyp8team2/auth/presentation/AuthControllerTest.java +++ b/src/test/java/com/swyp8team2/auth/presentation/AuthControllerTest.java @@ -19,6 +19,8 @@ import org.springframework.security.test.context.support.WithAnonymousUser; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.BDDMockito.given; import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName; import static org.springframework.restdocs.cookies.CookieDocumentation.requestCookies; @@ -170,4 +172,28 @@ void guestToken() throws Exception { ) )); } + + @Test + @DisplayName("로그아웃") + void signOut() throws Exception { + //given + + //when then + mockMvc.perform(post("/auth/sign-out") + .cookie(new Cookie(CustomHeader.CustomCookie.REFRESH_TOKEN, "refreshToken"))) + .andExpect(status().isOk()) + .andExpect(cookie().httpOnly(CustomHeader.CustomCookie.REFRESH_TOKEN, true)) + .andExpect(cookie().path(CustomHeader.CustomCookie.REFRESH_TOKEN, "/")) + .andExpect(cookie().secure(CustomHeader.CustomCookie.REFRESH_TOKEN, true)) + .andExpect(cookie().attribute(CustomHeader.CustomCookie.REFRESH_TOKEN, "SameSite", "None")) + .andExpect(cookie().maxAge(CustomHeader.CustomCookie.REFRESH_TOKEN, 0)) + .andDo(restDocs.document( + requestCookies( + cookieWithName(CustomHeader.CustomCookie.REFRESH_TOKEN).description("리프레시 토큰") + ), + responseCookies( + cookieWithName(CustomHeader.CustomCookie.REFRESH_TOKEN).description("리프레시 토큰") + ) + )); + } } From 86c7eca0db8afea215e3d8dc3cdfeaa4f60b75d2 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Sun, 2 Mar 2025 16:13:51 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:=20access=20token=20=EC=9E=88=EC=96=B4?= =?UTF-8?q?=EC=95=BC=20=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20=EA=B0=80?= =?UTF-8?q?=EB=8A=A5=ED=95=98=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/swyp8team2/auth/application/AuthService.java | 5 ++--- .../swyp8team2/auth/application/jwt/JwtService.java | 11 ++++++++--- .../swyp8team2/auth/presentation/AuthController.java | 7 +++++-- .../com/swyp8team2/common/config/SecurityConfig.java | 3 +-- .../auth/presentation/AuthControllerTest.java | 8 +++++++- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/swyp8team2/auth/application/AuthService.java b/src/main/java/com/swyp8team2/auth/application/AuthService.java index 17afa7d2..24b8c3fc 100644 --- a/src/main/java/com/swyp8team2/auth/application/AuthService.java +++ b/src/main/java/com/swyp8team2/auth/application/AuthService.java @@ -1,7 +1,6 @@ package com.swyp8team2.auth.application; import com.swyp8team2.auth.application.jwt.JwtService; -import com.swyp8team2.auth.application.jwt.TokenPair; import com.swyp8team2.auth.application.oauth.OAuthService; import com.swyp8team2.auth.application.oauth.dto.OAuthUserInfo; import com.swyp8team2.auth.domain.Provider; @@ -63,7 +62,7 @@ public String createGuestToken() { } @Transactional - public void signOut(String refreshToken) { - jwtService.signOut(refreshToken); + public void signOut(Long userId, String refreshToken) { + jwtService.signOut(userId, refreshToken); } } diff --git a/src/main/java/com/swyp8team2/auth/application/jwt/JwtService.java b/src/main/java/com/swyp8team2/auth/application/jwt/JwtService.java index 14a4e5df..75228fbd 100644 --- a/src/main/java/com/swyp8team2/auth/application/jwt/JwtService.java +++ b/src/main/java/com/swyp8team2/auth/application/jwt/JwtService.java @@ -46,8 +46,13 @@ public TokenResponse reissue(String refreshToken) { } @Transactional - public void signOut(String refreshToken) { - JwtClaim claim = jwtProvider.parseToken(refreshToken); - refreshTokenRepository.deleteByUserId(claim.idAsLong()); + public void signOut(Long userId, String refreshToken) { + RefreshToken token = refreshTokenRepository.findByUserId(userId) + .orElseThrow(() -> new BadRequestException(ErrorCode.REFRESH_TOKEN_NOT_FOUND)); + + if (!token.getToken().equals(refreshToken)) { + throw new BadRequestException(ErrorCode.REFRESH_TOKEN_MISMATCHED); + } + refreshTokenRepository.delete(token); } } diff --git a/src/main/java/com/swyp8team2/auth/presentation/AuthController.java b/src/main/java/com/swyp8team2/auth/presentation/AuthController.java index bb0a0f70..f0c6eeb4 100644 --- a/src/main/java/com/swyp8team2/auth/presentation/AuthController.java +++ b/src/main/java/com/swyp8team2/auth/presentation/AuthController.java @@ -3,6 +3,7 @@ import com.swyp8team2.auth.application.AuthService; import com.swyp8team2.auth.application.jwt.TokenPair; +import com.swyp8team2.auth.domain.UserInfo; import com.swyp8team2.auth.presentation.dto.GuestTokenResponse; import com.swyp8team2.auth.presentation.dto.OAuthSignInRequest; import com.swyp8team2.auth.presentation.dto.TokenResponse; @@ -15,6 +16,7 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -67,13 +69,14 @@ public ResponseEntity guestToken() { @PostMapping("/sign-out") public ResponseEntity signOut( @CookieValue(name = CustomHeader.CustomCookie.REFRESH_TOKEN, required = false) String refreshToken, - HttpServletResponse response + HttpServletResponse response, + @AuthenticationPrincipal UserInfo userInfo ) { if (Objects.isNull(refreshToken)) { throw new BadRequestException(ErrorCode.INVALID_REFRESH_TOKEN_HEADER); } refreshTokenCookieGenerator.removeCookie(response); - authService.signOut(refreshToken); + authService.signOut(userInfo.userId(), refreshToken); return ResponseEntity.ok().build(); } } diff --git a/src/main/java/com/swyp8team2/common/config/SecurityConfig.java b/src/main/java/com/swyp8team2/common/config/SecurityConfig.java index 0994c3fe..f283fa95 100644 --- a/src/main/java/com/swyp8team2/common/config/SecurityConfig.java +++ b/src/main/java/com/swyp8team2/common/config/SecurityConfig.java @@ -113,8 +113,7 @@ public static MvcRequestMatcher[] getWhiteList(HandlerMappingIntrospector intros mvc.pattern(HttpMethod.GET, "/posts/shareUrl/{shareUrl}"), mvc.pattern(HttpMethod.GET, "/posts/{postId}"), mvc.pattern(HttpMethod.GET, "/posts/{postId}/comments"), -// mvc.pattern("/posts/{postId}/votes/guest/**"), - mvc.pattern("/auth/oauth2/**") + mvc.pattern("/auth/oauth2/**"), }; } diff --git a/src/test/java/com/swyp8team2/auth/presentation/AuthControllerTest.java b/src/test/java/com/swyp8team2/auth/presentation/AuthControllerTest.java index 6b4c6a51..60390dfb 100644 --- a/src/test/java/com/swyp8team2/auth/presentation/AuthControllerTest.java +++ b/src/test/java/com/swyp8team2/auth/presentation/AuthControllerTest.java @@ -11,10 +11,12 @@ import com.swyp8team2.common.exception.ErrorResponse; import com.swyp8team2.common.presentation.CustomHeader; import com.swyp8team2.support.RestDocsTest; +import com.swyp8team2.support.WithMockUserInfo; import jakarta.servlet.http.Cookie; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithAnonymousUser; @@ -26,6 +28,7 @@ import static org.springframework.restdocs.cookies.CookieDocumentation.requestCookies; import static org.springframework.restdocs.cookies.CookieDocumentation.responseCookies; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; @@ -174,13 +177,15 @@ void guestToken() throws Exception { } @Test + @WithMockUserInfo @DisplayName("로그아웃") void signOut() throws Exception { //given //when then mockMvc.perform(post("/auth/sign-out") - .cookie(new Cookie(CustomHeader.CustomCookie.REFRESH_TOKEN, "refreshToken"))) + .cookie(new Cookie(CustomHeader.CustomCookie.REFRESH_TOKEN, "refreshToken")) + .header(HttpHeaders.AUTHORIZATION, "Bearer accessToken")) .andExpect(status().isOk()) .andExpect(cookie().httpOnly(CustomHeader.CustomCookie.REFRESH_TOKEN, true)) .andExpect(cookie().path(CustomHeader.CustomCookie.REFRESH_TOKEN, "/")) @@ -188,6 +193,7 @@ void signOut() throws Exception { .andExpect(cookie().attribute(CustomHeader.CustomCookie.REFRESH_TOKEN, "SameSite", "None")) .andExpect(cookie().maxAge(CustomHeader.CustomCookie.REFRESH_TOKEN, 0)) .andDo(restDocs.document( + requestHeaders(authorizationHeader()), requestCookies( cookieWithName(CustomHeader.CustomCookie.REFRESH_TOKEN).description("리프레시 토큰") ), From 6a42c82a8d7eee41ac8aa3957b647a5bf256029c Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Sun, 2 Mar 2025 16:14:48 +0900 Subject: [PATCH 4/6] =?UTF-8?q?fix:=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EB=B9=88=20=EA=B0=92=20=EB=93=A4=EC=96=B4?= =?UTF-8?q?=EC=98=AC=20=EB=95=8C=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../presentation/filter/GuestAuthFilter.java | 3 +++ .../crypto/application/CryptoService.java | 4 ++++ .../auth/application/JwtServiceTest.java | 18 +++++++++++++++--- .../crypto/application/CryptoServiceTest.java | 12 ++++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/swyp8team2/auth/presentation/filter/GuestAuthFilter.java b/src/main/java/com/swyp8team2/auth/presentation/filter/GuestAuthFilter.java index f1286b03..a7f6a006 100644 --- a/src/main/java/com/swyp8team2/auth/presentation/filter/GuestAuthFilter.java +++ b/src/main/java/com/swyp8team2/auth/presentation/filter/GuestAuthFilter.java @@ -50,6 +50,9 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse SecurityContextHolder.getContext().setAuthentication(authentication); } catch (ApplicationException e) { request.setAttribute(EXCEPTION_KEY, e); + } catch (Exception e) { + log.debug("GuestAuthFilter error", e); + request.setAttribute(EXCEPTION_KEY, new BadRequestException(ErrorCode.INVALID_TOKEN)); } finally { doFilter(request, response, filterChain); } diff --git a/src/main/java/com/swyp8team2/crypto/application/CryptoService.java b/src/main/java/com/swyp8team2/crypto/application/CryptoService.java index 9803147d..4ff34182 100644 --- a/src/main/java/com/swyp8team2/crypto/application/CryptoService.java +++ b/src/main/java/com/swyp8team2/crypto/application/CryptoService.java @@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.crypto.encrypt.AesBytesEncryptor; +import org.springframework.util.StringUtils; import java.nio.charset.StandardCharsets; @@ -28,6 +29,9 @@ public String encrypt(String data) { public String decrypt(String encryptedData) { try { + if (!StringUtils.hasText(encryptedData)) { + throw new InternalServerException(ErrorCode.INVALID_TOKEN); + } byte[] decryptBytes = Base62.createInstance().decode(encryptedData.getBytes(StandardCharsets.UTF_8)); byte[] decrypt = encryptor.decrypt(decryptBytes); return new String(decrypt, StandardCharsets.UTF_8); diff --git a/src/test/java/com/swyp8team2/auth/application/JwtServiceTest.java b/src/test/java/com/swyp8team2/auth/application/JwtServiceTest.java index c39f8035..40cefbdb 100644 --- a/src/test/java/com/swyp8team2/auth/application/JwtServiceTest.java +++ b/src/test/java/com/swyp8team2/auth/application/JwtServiceTest.java @@ -111,14 +111,26 @@ void signOut() throws Exception { //given long givenUserId = 1L; String givenRefreshToken = "refreshToken"; - given(jwtProvider.parseToken(eq(givenRefreshToken))) - .willReturn(new JwtClaim(givenUserId)); refreshTokenRepository.save(new RefreshToken(givenUserId, givenRefreshToken)); //when - jwtService.signOut(givenRefreshToken); + jwtService.signOut(givenUserId, givenRefreshToken); //then assertThat(refreshTokenRepository.findByUserId(givenUserId)).isEmpty(); } + + @Test + @DisplayName("로그아웃 - 유저의 refresh token이 아닌 경우") + void signOut_invalidRefreshToken() throws Exception { + //given + long givenUserId = 1L; + String givenRefreshToken = "refreshToken"; + refreshTokenRepository.save(new RefreshToken(givenUserId, givenRefreshToken)); + + //when then + assertThatThrownBy(() -> jwtService.signOut(givenUserId, "differentToken")) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.REFRESH_TOKEN_MISMATCHED.getMessage()); + } } diff --git a/src/test/java/com/swyp8team2/crypto/application/CryptoServiceTest.java b/src/test/java/com/swyp8team2/crypto/application/CryptoServiceTest.java index 90e26e0d..89b9fd1e 100644 --- a/src/test/java/com/swyp8team2/crypto/application/CryptoServiceTest.java +++ b/src/test/java/com/swyp8team2/crypto/application/CryptoServiceTest.java @@ -60,4 +60,16 @@ void decrypt_invalidToken() { .isInstanceOf(BadRequestException.class) .hasMessage(ErrorCode.INVALID_TOKEN.getMessage()); } + + @Test + @DisplayName("복호화 - empty string") + void decrypt_emptyString() { + // given + String invalid = ""; + + // when then + assertThatThrownBy(() -> cryptoService.decrypt(invalid)) + .isInstanceOf(BadRequestException.class) + .hasMessage(ErrorCode.INVALID_TOKEN.getMessage()); + } } From 58bab13008382fae3b8caddde72e9ce5dbfbc90d Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Sun, 2 Mar 2025 16:14:58 +0900 Subject: [PATCH 5/6] =?UTF-8?q?docs:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20docs=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/auth.adoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/docs/asciidoc/auth.adoc b/src/docs/asciidoc/auth.adoc index 683a2657..bf0d05db 100644 --- a/src/docs/asciidoc/auth.adoc +++ b/src/docs/asciidoc/auth.adoc @@ -15,3 +15,8 @@ operation::auth-controller-test/reissue[snippets='http-request,curl-request,requ === `POST` 게스트 토큰 발급 operation::auth-controller-test/guest-token[snippets='http-request,curl-request,http-response,response-fields'] + +[[로그아웃]] +=== `POST` 로그아웃 + +operation::auth-controller-test/sign-out[snippets='http-request,curl-request,request-cookies,request-headers,http-response,response-cookies'] From 1a3e9074ddeb836960e0a940002749f0ce28b053 Mon Sep 17 00:00:00 2001 From: wlsh44 Date: Sun, 2 Mar 2025 16:22:58 +0900 Subject: [PATCH 6/6] =?UTF-8?q?refactor:=20=EB=82=B4=EA=B0=80=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1/=ED=88=AC=ED=91=9C=ED=95=9C=20->=20=EC=9C=A0=EC=A0=80?= =?UTF-8?q?=EA=B0=80=20=EC=9E=91=EC=84=B1/=ED=88=AC=ED=91=9C=ED=95=9C=20ap?= =?UTF-8?q?i=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../swyp8team2/post/application/PostService.java | 3 +-- .../post/presentation/PostController.java | 16 ++++++++-------- .../post/application/PostServiceTest.java | 15 +++++++-------- .../post/presentation/PostControllerTest.java | 12 +++++++----- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/swyp8team2/post/application/PostService.java b/src/main/java/com/swyp8team2/post/application/PostService.java index 72ff11fa..f48b9c13 100644 --- a/src/main/java/com/swyp8team2/post/application/PostService.java +++ b/src/main/java/com/swyp8team2/post/application/PostService.java @@ -21,7 +21,6 @@ import com.swyp8team2.user.domain.UserRepository; import com.swyp8team2.vote.domain.Vote; import com.swyp8team2.vote.domain.VoteRepository; -import lombok.RequiredArgsConstructor; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import org.springframework.stereotype.Service; @@ -113,7 +112,7 @@ private Boolean getVoted(PostImage image, Long userId, Long postId) { .orElse(false); } - public CursorBasePaginatedResponse findMyPosts(Long userId, Long cursor, int size) { + public CursorBasePaginatedResponse findUserPosts(Long userId, Long cursor, int size) { Slice postSlice = postRepository.findByUserId(userId, cursor, PageRequest.ofSize(size)); return CursorBasePaginatedResponse.of(postSlice.map(this::createSimplePostResponse) ); diff --git a/src/main/java/com/swyp8team2/post/presentation/PostController.java b/src/main/java/com/swyp8team2/post/presentation/PostController.java index 52c1cb81..625897dd 100644 --- a/src/main/java/com/swyp8team2/post/presentation/PostController.java +++ b/src/main/java/com/swyp8team2/post/presentation/PostController.java @@ -88,21 +88,21 @@ public ResponseEntity deletePost( return ResponseEntity.ok().build(); } - @GetMapping("/user/me") + @GetMapping("/users/{userId}") public ResponseEntity> findMyPosts( + @PathVariable("userId") Long userId, @RequestParam(name = "cursor", required = false) @Min(0) Long cursor, - @RequestParam(name = "size", required = false, defaultValue = "10") @Min(1) int size, - @AuthenticationPrincipal UserInfo userInfo + @RequestParam(name = "size", required = false, defaultValue = "10") @Min(1) int size ) { - return ResponseEntity.ok(postService.findMyPosts(userInfo.userId(), cursor, size)); + return ResponseEntity.ok(postService.findUserPosts(userId, cursor, size)); } - @GetMapping("/user/voted") + @GetMapping("/users/{userId}/voted") public ResponseEntity> findVotedPosts( + @PathVariable("userId") Long userId, @RequestParam(name = "cursor", required = false) @Min(0) Long cursor, - @RequestParam(name = "size", required = false, defaultValue = "10") @Min(1) int size, - @AuthenticationPrincipal UserInfo userInfo + @RequestParam(name = "size", required = false, defaultValue = "10") @Min(1) int size ) { - return ResponseEntity.ok(postService.findVotedPosts(userInfo.userId(), cursor, size)); + return ResponseEntity.ok(postService.findVotedPosts(userId, cursor, size)); } } diff --git a/src/test/java/com/swyp8team2/post/application/PostServiceTest.java b/src/test/java/com/swyp8team2/post/application/PostServiceTest.java index 4d06c2f3..af97d0a1 100644 --- a/src/test/java/com/swyp8team2/post/application/PostServiceTest.java +++ b/src/test/java/com/swyp8team2/post/application/PostServiceTest.java @@ -25,7 +25,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.bean.override.mockito.MockitoBean; -import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import java.util.ArrayList; import java.util.List; @@ -156,15 +155,15 @@ void findById() throws Exception { } @Test - @DisplayName("내가 작성한 게시글 조회 - 커서 null인 경우") - void findMyPosts() throws Exception { + @DisplayName("유저가 작성한 게시글 조회 - 커서 null인 경우") + void findUserPosts() throws Exception { //given User user = userRepository.save(createUser(1)); List posts = createPosts(user); int size = 10; //when - var response = postService.findMyPosts(user.getId(), null, size); + var response = postService.findUserPosts(user.getId(), null, size); //then assertAll( @@ -175,15 +174,15 @@ void findMyPosts() throws Exception { } @Test - @DisplayName("내가 작성한 게시글 조회 - 커서 있는 경우") - void findMyPosts2() throws Exception { + @DisplayName("유저가 작성한 게시글 조회 - 커서 있는 경우") + void findUserPosts2() throws Exception { //given User user = userRepository.save(createUser(1)); List posts = createPosts(user); int size = 10; //when - var response = postService.findMyPosts(user.getId(), posts.get(3).getId(), size); + var response = postService.findUserPosts(user.getId(), posts.get(3).getId(), size); //then assertAll( @@ -204,7 +203,7 @@ private List createPosts(User user) { } @Test - @DisplayName("내가 투표한 게시글 조회 - 커서 null인 경우") + @DisplayName("유저가 투표한 게시글 조회 - 커서 null인 경우") void findVotedPosts() throws Exception { //given User user = userRepository.save(createUser(1)); diff --git a/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java b/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java index aff536f4..befad202 100644 --- a/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java +++ b/src/test/java/com/swyp8team2/post/presentation/PostControllerTest.java @@ -248,7 +248,7 @@ void deletePost() throws Exception { @Test @WithMockUserInfo - @DisplayName("내가 작성한 게시글 조회") + @DisplayName("유저가 작성한 게시글 조회") void findMyPost() throws Exception { //given var response = new CursorBasePaginatedResponse<>( @@ -263,15 +263,16 @@ void findMyPost() throws Exception { ) ) ); - given(postService.findMyPosts(1L, null, 10)) + given(postService.findUserPosts(1L, null, 10)) .willReturn(response); //when then - mockMvc.perform(get("/posts/user/me") + mockMvc.perform(RestDocumentationRequestBuilders.get("/posts/users/{userId}", 1) .header(HttpHeaders.AUTHORIZATION, "Bearer token")) .andExpect(status().isOk()) .andExpect(content().json(objectMapper.writeValueAsString(response))) .andDo(restDocs.document( + pathParameters(parameterWithName("userId").description("유저 Id")), requestHeaders(authorizationHeader()), queryParameters(cursorQueryParams()), responseFields( @@ -303,7 +304,7 @@ void findMyPost() throws Exception { @Test @WithMockUserInfo - @DisplayName("내가 참여한 게시글 조회") + @DisplayName("유저가 참여한 게시글 조회") void findVotedPost() throws Exception { //given var response = new CursorBasePaginatedResponse<>( @@ -322,11 +323,12 @@ void findVotedPost() throws Exception { .willReturn(response); //when then - mockMvc.perform(get("/posts/user/voted") + mockMvc.perform(RestDocumentationRequestBuilders.get("/posts/users/{userId}/voted", 1) .header(HttpHeaders.AUTHORIZATION, "Bearer token")) .andExpect(status().isOk()) .andExpect(content().json(objectMapper.writeValueAsString(response))) .andDo(restDocs.document( + pathParameters(parameterWithName("userId").description("유저 Id")), requestHeaders(authorizationHeader()), queryParameters(cursorQueryParams()), responseFields(