From dd7dd47de3a6c0e3a80e91260f17a6f30c5a9e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:15:17 +0900 Subject: [PATCH 01/40] =?UTF-8?q?refactor(package):=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/somemore/auth/{ => jwt/filter}/UserRole.java | 2 +- .../volunteer/service/{query => }/FindVolunteerIdService.java | 4 ++-- .../service/{command => }/RegisterVolunteerService.java | 4 ++-- .../volunteer/usecase/{query => }/FindVolunteerIdUseCase.java | 2 +- .../usecase/{command => }/RegisterVolunteerUseCase.java | 2 +- .../service/{query => }/FindVolunteerIdServiceTest.java | 2 +- .../service/{command => }/RegisterVolunteerServiceTest.java | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) rename src/main/java/com/somemore/auth/{ => jwt/filter}/UserRole.java (62%) rename src/main/java/com/somemore/volunteer/service/{query => }/FindVolunteerIdService.java (91%) rename src/main/java/com/somemore/volunteer/service/{command => }/RegisterVolunteerService.java (90%) rename src/main/java/com/somemore/volunteer/usecase/{query => }/FindVolunteerIdUseCase.java (87%) rename src/main/java/com/somemore/volunteer/usecase/{command => }/RegisterVolunteerUseCase.java (78%) rename src/test/java/com/somemore/volunteer/service/{query => }/FindVolunteerIdServiceTest.java (98%) rename src/test/java/com/somemore/volunteer/service/{command => }/RegisterVolunteerServiceTest.java (98%) diff --git a/src/main/java/com/somemore/auth/UserRole.java b/src/main/java/com/somemore/auth/jwt/filter/UserRole.java similarity index 62% rename from src/main/java/com/somemore/auth/UserRole.java rename to src/main/java/com/somemore/auth/jwt/filter/UserRole.java index 0e4d9ed8e..857ee5c8a 100644 --- a/src/main/java/com/somemore/auth/UserRole.java +++ b/src/main/java/com/somemore/auth/jwt/filter/UserRole.java @@ -1,4 +1,4 @@ -package com.somemore.auth; +package com.somemore.auth.jwt.filter; public enum UserRole { VOLUNTEER, diff --git a/src/main/java/com/somemore/volunteer/service/query/FindVolunteerIdService.java b/src/main/java/com/somemore/volunteer/service/FindVolunteerIdService.java similarity index 91% rename from src/main/java/com/somemore/volunteer/service/query/FindVolunteerIdService.java rename to src/main/java/com/somemore/volunteer/service/FindVolunteerIdService.java index 970a643d4..7825aa367 100644 --- a/src/main/java/com/somemore/volunteer/service/query/FindVolunteerIdService.java +++ b/src/main/java/com/somemore/volunteer/service/FindVolunteerIdService.java @@ -1,9 +1,9 @@ -package com.somemore.volunteer.service.query; +package com.somemore.volunteer.service; import com.somemore.volunteer.domain.Volunteer; import com.somemore.volunteer.dto.response.VolunteerForCommunityResponseDto; import com.somemore.volunteer.repository.VolunteerRepository; -import com.somemore.volunteer.usecase.query.FindVolunteerIdUseCase; +import com.somemore.volunteer.usecase.FindVolunteerIdUseCase; import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/com/somemore/volunteer/service/command/RegisterVolunteerService.java b/src/main/java/com/somemore/volunteer/service/RegisterVolunteerService.java similarity index 90% rename from src/main/java/com/somemore/volunteer/service/command/RegisterVolunteerService.java rename to src/main/java/com/somemore/volunteer/service/RegisterVolunteerService.java index 8ea14dea0..8feb930c1 100644 --- a/src/main/java/com/somemore/volunteer/service/command/RegisterVolunteerService.java +++ b/src/main/java/com/somemore/volunteer/service/RegisterVolunteerService.java @@ -1,11 +1,11 @@ -package com.somemore.volunteer.service.command; +package com.somemore.volunteer.service; import com.somemore.volunteer.domain.Volunteer; import com.somemore.volunteer.domain.VolunteerDetail; import com.somemore.volunteer.dto.request.VolunteerRegisterRequestDto; import com.somemore.volunteer.repository.VolunteerDetailRepository; import com.somemore.volunteer.repository.VolunteerRepository; -import com.somemore.volunteer.usecase.command.RegisterVolunteerUseCase; +import com.somemore.volunteer.usecase.RegisterVolunteerUseCase; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/somemore/volunteer/usecase/query/FindVolunteerIdUseCase.java b/src/main/java/com/somemore/volunteer/usecase/FindVolunteerIdUseCase.java similarity index 87% rename from src/main/java/com/somemore/volunteer/usecase/query/FindVolunteerIdUseCase.java rename to src/main/java/com/somemore/volunteer/usecase/FindVolunteerIdUseCase.java index cf1775afb..e23eaa20f 100644 --- a/src/main/java/com/somemore/volunteer/usecase/query/FindVolunteerIdUseCase.java +++ b/src/main/java/com/somemore/volunteer/usecase/FindVolunteerIdUseCase.java @@ -1,4 +1,4 @@ -package com.somemore.volunteer.usecase.query; +package com.somemore.volunteer.usecase; import com.somemore.volunteer.dto.response.VolunteerForCommunityResponseDto; diff --git a/src/main/java/com/somemore/volunteer/usecase/command/RegisterVolunteerUseCase.java b/src/main/java/com/somemore/volunteer/usecase/RegisterVolunteerUseCase.java similarity index 78% rename from src/main/java/com/somemore/volunteer/usecase/command/RegisterVolunteerUseCase.java rename to src/main/java/com/somemore/volunteer/usecase/RegisterVolunteerUseCase.java index 45e06dcf4..0e90d980d 100644 --- a/src/main/java/com/somemore/volunteer/usecase/command/RegisterVolunteerUseCase.java +++ b/src/main/java/com/somemore/volunteer/usecase/RegisterVolunteerUseCase.java @@ -1,4 +1,4 @@ -package com.somemore.volunteer.usecase.command; +package com.somemore.volunteer.usecase; import com.somemore.volunteer.dto.request.VolunteerRegisterRequestDto; diff --git a/src/test/java/com/somemore/volunteer/service/query/FindVolunteerIdServiceTest.java b/src/test/java/com/somemore/volunteer/service/FindVolunteerIdServiceTest.java similarity index 98% rename from src/test/java/com/somemore/volunteer/service/query/FindVolunteerIdServiceTest.java rename to src/test/java/com/somemore/volunteer/service/FindVolunteerIdServiceTest.java index 4bd0ec03b..831348722 100644 --- a/src/test/java/com/somemore/volunteer/service/query/FindVolunteerIdServiceTest.java +++ b/src/test/java/com/somemore/volunteer/service/FindVolunteerIdServiceTest.java @@ -1,4 +1,4 @@ -package com.somemore.volunteer.service.query; +package com.somemore.volunteer.service; import com.somemore.IntegrationTestSupport; import com.somemore.auth.oauth.OAuthProvider; diff --git a/src/test/java/com/somemore/volunteer/service/command/RegisterVolunteerServiceTest.java b/src/test/java/com/somemore/volunteer/service/RegisterVolunteerServiceTest.java similarity index 98% rename from src/test/java/com/somemore/volunteer/service/command/RegisterVolunteerServiceTest.java rename to src/test/java/com/somemore/volunteer/service/RegisterVolunteerServiceTest.java index 8454a470d..543718d3a 100644 --- a/src/test/java/com/somemore/volunteer/service/command/RegisterVolunteerServiceTest.java +++ b/src/test/java/com/somemore/volunteer/service/RegisterVolunteerServiceTest.java @@ -1,4 +1,4 @@ -package com.somemore.volunteer.service.command; +package com.somemore.volunteer.service; import com.somemore.IntegrationTestSupport; import com.somemore.auth.oauth.OAuthProvider; From 8fb254c89f49e7665f77280f66741966f970ebd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:16:05 +0900 Subject: [PATCH 02/40] =?UTF-8?q?style(ApiResponse):=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EA=B0=9C=ED=96=89=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/somemore/global/common/response/ApiResponse.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/com/somemore/global/common/response/ApiResponse.java b/src/main/java/com/somemore/global/common/response/ApiResponse.java index 3b2b32ef5..ebdd60c3a 100644 --- a/src/main/java/com/somemore/global/common/response/ApiResponse.java +++ b/src/main/java/com/somemore/global/common/response/ApiResponse.java @@ -12,26 +12,20 @@ public class ApiResponse { private T data; public static ApiResponse ok(int status, T data, String message) { - return new ApiResponse<>(status, message, data); } public static ApiResponse ok(String message) { - return new ApiResponse<>(200, message, ""); } public static ApiResponse error(int code, String message) { - return new ApiResponse<>(code, message, ""); } - - public ApiResponse(int code, String message, T data) { this.code = code; this.message = message; this.data = data; } - } From c54b5698cf823c7596f8b570e82e4cba85757ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:17:01 +0900 Subject: [PATCH 03/40] =?UTF-8?q?feat(application.yml):=20front,=20back=20?= =?UTF-8?q?root=20url=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 7 ++++--- src/test/resources/application-test.yml | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e6943650b..b4eb6c368 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,3 +1,7 @@ +app: + front-url: "http://localhost:3000" + back-url: "http://localhost:8080" + spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver @@ -58,9 +62,6 @@ springdoc: paths-to-match: - /api/** -frontend: - url: localhost - jwt: secret: ${JWT_SECRET} diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index a80bba3cc..a53533e3e 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -1,3 +1,7 @@ +app: + front-url: "http://localhost:3000" + back-url: "http://localhost:8080" + spring: config: activate: @@ -27,9 +31,5 @@ spring: port: 6379 password: # 테스트에서는 비밀번호 없이 연결 -frontend: - url: http://localhost:3000 # 테스트용 프론트엔드 주소 - jwt: secret: 63bf2c80266cd25072e53b3482e318c30d1cd18d8c98d0f5d278530a94fe28d9fbbec531e5ccb58c725c125738182357786b71f43a7172c5d0c94a17f0da44f2 # 테스트용 JWT 시크릿 키 - From e27ff41063be1f06f86a29e5caf518016552801d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:18:29 +0900 Subject: [PATCH 04/40] =?UTF-8?q?refactor(package):=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/somemore/auth/jwt/filter/JwtAuthFilter.java | 1 - .../oauth/naver/service/command/NaverOAuth2UserInfoService.java | 2 +- .../somemore/community/service/CommunityBoardQueryService.java | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/somemore/auth/jwt/filter/JwtAuthFilter.java b/src/main/java/com/somemore/auth/jwt/filter/JwtAuthFilter.java index 3f9a2c677..08ced03a6 100644 --- a/src/main/java/com/somemore/auth/jwt/filter/JwtAuthFilter.java +++ b/src/main/java/com/somemore/auth/jwt/filter/JwtAuthFilter.java @@ -1,6 +1,5 @@ package com.somemore.auth.jwt.filter; -import com.somemore.auth.UserRole; import com.somemore.auth.jwt.domain.EncodedToken; import com.somemore.auth.jwt.exception.JwtErrorType; import com.somemore.auth.jwt.exception.JwtException; diff --git a/src/main/java/com/somemore/auth/oauth/naver/service/command/NaverOAuth2UserInfoService.java b/src/main/java/com/somemore/auth/oauth/naver/service/command/NaverOAuth2UserInfoService.java index 496dcf1f3..cf6f92d02 100644 --- a/src/main/java/com/somemore/auth/oauth/naver/service/command/NaverOAuth2UserInfoService.java +++ b/src/main/java/com/somemore/auth/oauth/naver/service/command/NaverOAuth2UserInfoService.java @@ -4,7 +4,7 @@ import com.somemore.auth.oauth.naver.usecase.query.CheckNaverUserUseCase; import com.somemore.auth.oauth.naver.usecase.command.RegisterNaverUserUseCase; import com.somemore.auth.oauth.naver.util.OAuthResponseConverter; -import com.somemore.volunteer.usecase.command.RegisterVolunteerUseCase; +import com.somemore.volunteer.usecase.RegisterVolunteerUseCase; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.oauth2.core.user.OAuth2User; diff --git a/src/main/java/com/somemore/community/service/CommunityBoardQueryService.java b/src/main/java/com/somemore/community/service/CommunityBoardQueryService.java index 00aeb74b8..aca97f3f7 100644 --- a/src/main/java/com/somemore/community/service/CommunityBoardQueryService.java +++ b/src/main/java/com/somemore/community/service/CommunityBoardQueryService.java @@ -9,7 +9,7 @@ import com.somemore.community.usecase.CommunityBoardQueryUseCase; import com.somemore.global.exception.BadRequestException; import com.somemore.volunteer.dto.response.VolunteerForCommunityResponseDto; -import com.somemore.volunteer.usecase.query.FindVolunteerIdUseCase; +import com.somemore.volunteer.usecase.FindVolunteerIdUseCase; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; From 0304e0884a2e26ca9391943b5bd3ac96bcedbb17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:20:31 +0900 Subject: [PATCH 05/40] =?UTF-8?q?feat(cookie):=20=EC=BF=A0=ED=82=A4=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C(=EB=8D=AE=EC=96=B4=EC=94=8C=EC=9A=B0?= =?UTF-8?q?=EA=B8=B0),=20=ED=86=A0=ED=81=B0=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80(SIGNOUT),=20=ED=8C=8C=EC=9D=BC=EB=AA=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../somemore/auth/cookie/CookieService.java | 36 +++++++++++++++++++ .../somemore/auth/cookie/CookieUseCase.java | 9 +++++ .../auth/cookie/SetCookieService.java | 30 ---------------- .../auth/cookie/SetCookieUseCase.java | 8 ----- .../somemore/auth/jwt/domain/TokenType.java | 3 +- .../somemore/auth/jwt/service/JwtService.java | 6 ++-- .../success/CustomOAuthSuccessHandler.java | 11 +++--- 7 files changed, 55 insertions(+), 48 deletions(-) create mode 100644 src/main/java/com/somemore/auth/cookie/CookieService.java create mode 100644 src/main/java/com/somemore/auth/cookie/CookieUseCase.java delete mode 100644 src/main/java/com/somemore/auth/cookie/SetCookieService.java delete mode 100644 src/main/java/com/somemore/auth/cookie/SetCookieUseCase.java diff --git a/src/main/java/com/somemore/auth/cookie/CookieService.java b/src/main/java/com/somemore/auth/cookie/CookieService.java new file mode 100644 index 000000000..f68080cca --- /dev/null +++ b/src/main/java/com/somemore/auth/cookie/CookieService.java @@ -0,0 +1,36 @@ +package com.somemore.auth.cookie; + +import com.somemore.auth.jwt.domain.TokenType; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseCookie; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +@Slf4j +public class CookieService implements CookieUseCase { + + @Override + public void setAccessToken(HttpServletResponse response, String value) { + ResponseCookie cookie = generateCookie(TokenType.ACCESS, value); + response.addHeader("Set-Cookie", cookie.toString()); + } + + @Override + public void deleteAccessToken(HttpServletResponse response) { + ResponseCookie cookie = generateCookie(TokenType.SIGNOUT, TokenType.SIGNOUT.name()); + response.addHeader("Set-Cookie", cookie.toString()); + } + + private static ResponseCookie generateCookie(TokenType tokenType, String value) { + return ResponseCookie.from(TokenType.ACCESS.name(), value) // 덮어쓰기 위해서 고정 값 + .httpOnly(true) + .secure(true) + .path("/") + .maxAge(tokenType.getPeriodInSeconds()) + .sameSite("Lax") + .build(); + } +} diff --git a/src/main/java/com/somemore/auth/cookie/CookieUseCase.java b/src/main/java/com/somemore/auth/cookie/CookieUseCase.java new file mode 100644 index 000000000..40f5e04f5 --- /dev/null +++ b/src/main/java/com/somemore/auth/cookie/CookieUseCase.java @@ -0,0 +1,9 @@ +package com.somemore.auth.cookie; + +import jakarta.servlet.http.HttpServletResponse; + +public interface CookieUseCase { + void setAccessToken(HttpServletResponse response, String value); + + void deleteAccessToken(HttpServletResponse response); +} diff --git a/src/main/java/com/somemore/auth/cookie/SetCookieService.java b/src/main/java/com/somemore/auth/cookie/SetCookieService.java deleted file mode 100644 index f728dac31..000000000 --- a/src/main/java/com/somemore/auth/cookie/SetCookieService.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.somemore.auth.cookie; - -import com.somemore.auth.jwt.domain.TokenType; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseCookie; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -@Slf4j -public class SetCookieService implements SetCookieUseCase { - - @Override - public void setToken(HttpServletResponse response, String value, TokenType tokenType) { - ResponseCookie cookie = generateCookie(tokenType.name(), value, tokenType.getPeriodInSeconds()); - response.addHeader("Set-Cookie", cookie.toString()); - } - - private static ResponseCookie generateCookie(String name, String value, int time) { - return ResponseCookie.from(name, value) - .httpOnly(true) - .secure(true) - .path("/") - .maxAge(time) - .sameSite("Lax") - .build(); - } -} diff --git a/src/main/java/com/somemore/auth/cookie/SetCookieUseCase.java b/src/main/java/com/somemore/auth/cookie/SetCookieUseCase.java deleted file mode 100644 index 148e2e013..000000000 --- a/src/main/java/com/somemore/auth/cookie/SetCookieUseCase.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.somemore.auth.cookie; - -import com.somemore.auth.jwt.domain.TokenType; -import jakarta.servlet.http.HttpServletResponse; - -public interface SetCookieUseCase { - void setToken(HttpServletResponse response, String value, TokenType tokenType); -} diff --git a/src/main/java/com/somemore/auth/jwt/domain/TokenType.java b/src/main/java/com/somemore/auth/jwt/domain/TokenType.java index 55a4e88f4..e6ea4638e 100644 --- a/src/main/java/com/somemore/auth/jwt/domain/TokenType.java +++ b/src/main/java/com/somemore/auth/jwt/domain/TokenType.java @@ -5,7 +5,8 @@ @Getter public enum TokenType { ACCESS(1000 * 60 * 30), - REFRESH(1000 * 60 * 60 * 24 * 7); + REFRESH(1000 * 60 * 60 * 24 * 7), + SIGNOUT(0); private final int period; diff --git a/src/main/java/com/somemore/auth/jwt/service/JwtService.java b/src/main/java/com/somemore/auth/jwt/service/JwtService.java index dd84f0b20..6bf18ba98 100644 --- a/src/main/java/com/somemore/auth/jwt/service/JwtService.java +++ b/src/main/java/com/somemore/auth/jwt/service/JwtService.java @@ -1,6 +1,6 @@ package com.somemore.auth.jwt.service; -import com.somemore.auth.cookie.SetCookieUseCase; +import com.somemore.auth.cookie.CookieUseCase; import com.somemore.auth.jwt.domain.EncodedToken; import com.somemore.auth.jwt.domain.TokenType; import com.somemore.auth.jwt.exception.JwtErrorType; @@ -24,7 +24,7 @@ public class JwtService implements JwtUseCase { private final JwtParser jwtParser; private final JwtValidator jwtValidator; private final JwtRefresher jwtRefresher; - private final SetCookieUseCase setCookieUseCase; + private final CookieUseCase cookieUseCase; @Override public EncodedToken generateToken(String userId, String role, TokenType tokenType) { @@ -48,7 +48,7 @@ public Claims getClaims(EncodedToken token) { private void handleJwtExpiredException(JwtException e, EncodedToken accessToken, HttpServletResponse response) { if (e.getErrorType() == JwtErrorType.EXPIRED_TOKEN) { EncodedToken refreshedToken = jwtRefresher.refreshAccessToken(accessToken); - setCookieUseCase.setToken(response, refreshedToken.value(), TokenType.ACCESS); + cookieUseCase.setAccessToken(response, refreshedToken.value()); return; } throw e; diff --git a/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java b/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java index 5f389367b..d26edadfd 100644 --- a/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java +++ b/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java @@ -1,13 +1,12 @@ package com.somemore.auth.oauth.handler.success; -import com.somemore.auth.cookie.SetCookieUseCase; +import com.somemore.auth.cookie.CookieUseCase; import com.somemore.auth.jwt.domain.EncodedToken; -import com.somemore.auth.jwt.domain.TokenType; import com.somemore.auth.jwt.usecase.command.GenerateTokensOnLoginUseCase; import com.somemore.auth.oauth.OAuthProvider; import com.somemore.auth.oauth.naver.service.query.ProcessNaverOAuthUserService; import com.somemore.auth.redirect.RedirectUseCase; -import com.somemore.volunteer.usecase.query.FindVolunteerIdUseCase; +import com.somemore.volunteer.usecase.FindVolunteerIdUseCase; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -29,10 +28,10 @@ public class CustomOAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHan private final ProcessNaverOAuthUserService processNaverOAuthService; private final FindVolunteerIdUseCase findVolunteerIdUseCase; private final GenerateTokensOnLoginUseCase generateTokensOnLoginUseCase; - private final SetCookieUseCase setCookieUseCase; + private final CookieUseCase cookieUseCase; private final RedirectUseCase redirectUseCase; - @Value("${frontend.url}") + @Value("${app.front-url}") private String frontendRootUrl; @Override @@ -49,7 +48,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo UUID volunteerId = findVolunteerIdUseCase.findVolunteerIdByOAuthId(oAuthId); EncodedToken accessToken = generateTokensOnLoginUseCase.saveRefreshTokenAndReturnAccessToken(volunteerId); - setCookieUseCase.setToken(response, accessToken.value(), TokenType.ACCESS); + cookieUseCase.setAccessToken(response, accessToken.value()); redirectUseCase.redirect(request, response, frontendRootUrl); } From 5be10965d4ab8e6578252e961390f73af39b801d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:24:29 +0900 Subject: [PATCH 06/40] =?UTF-8?q?feat(RefreshToken):=20=EB=A6=AC=ED=94=84?= =?UTF-8?q?=EB=A0=88=EC=8B=9C=20=ED=86=A0=ED=81=B0=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 리프레시 토큰 삭제의 파라미터를 엑세스 토큰에서 사용자 ID로 변경 - 리프레시 토큰이 존재하지 않아도 로그아웃이 가능하도록 로직 수정 - 리프레시 토큰이 없는 상태에서 로그아웃이 불가능한 것은 비정상적인 동작으로 판단 - 레포지토리에서 사용자 ID로 리프레시 토큰을 찾는 메서드 추가 --- .../jwt/refresh/manager/RedisRefreshTokenManager.java | 8 +++----- .../auth/jwt/refresh/manager/RefreshTokenManager.java | 2 +- .../jwt/refresh/repository/RefreshTokenRepository.java | 2 ++ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/somemore/auth/jwt/refresh/manager/RedisRefreshTokenManager.java b/src/main/java/com/somemore/auth/jwt/refresh/manager/RedisRefreshTokenManager.java index 0a2d57e64..9f717056c 100644 --- a/src/main/java/com/somemore/auth/jwt/refresh/manager/RedisRefreshTokenManager.java +++ b/src/main/java/com/somemore/auth/jwt/refresh/manager/RedisRefreshTokenManager.java @@ -26,10 +26,8 @@ public void save(RefreshToken refreshToken) { } @Override - public void removeRefreshToken(EncodedToken accessToken) { - RefreshToken refreshToken = refreshTokenRepository.findByAccessToken(accessToken.value()) - .orElseThrow(() -> new JwtException(JwtErrorType.EXPIRED_TOKEN)); - - refreshTokenRepository.delete(refreshToken); + public void removeRefreshToken(String userId) { + refreshTokenRepository.findByUserId(userId) + .ifPresent(refreshTokenRepository::delete); } } diff --git a/src/main/java/com/somemore/auth/jwt/refresh/manager/RefreshTokenManager.java b/src/main/java/com/somemore/auth/jwt/refresh/manager/RefreshTokenManager.java index eb27a6582..ec2c0b1a7 100644 --- a/src/main/java/com/somemore/auth/jwt/refresh/manager/RefreshTokenManager.java +++ b/src/main/java/com/somemore/auth/jwt/refresh/manager/RefreshTokenManager.java @@ -8,5 +8,5 @@ public interface RefreshTokenManager { void save(RefreshToken refreshToken); - void removeRefreshToken(EncodedToken accessToken); + void removeRefreshToken(String userId); } diff --git a/src/main/java/com/somemore/auth/jwt/refresh/repository/RefreshTokenRepository.java b/src/main/java/com/somemore/auth/jwt/refresh/repository/RefreshTokenRepository.java index 9eb75e24a..31a566751 100644 --- a/src/main/java/com/somemore/auth/jwt/refresh/repository/RefreshTokenRepository.java +++ b/src/main/java/com/somemore/auth/jwt/refresh/repository/RefreshTokenRepository.java @@ -10,4 +10,6 @@ public interface RefreshTokenRepository extends CrudRepository { Optional findByAccessToken(String accessToken); + + Optional findByUserId(String userId); } From a5c6dd6348312c1e6a8b6904cfd90cca2cc94e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:27:01 +0900 Subject: [PATCH 07/40] =?UTF-8?q?feat(SignOut):=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EB=B0=8F=20=EB=A6=AC=ED=94=84=EB=A0=88=EC=8B=9C=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EC=82=AD=EC=A0=9C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로그아웃 시 액세스 토큰 쿠키를 삭제(덮어씌우기)하는 로직 추가 - 리프레시 토큰을 사용자 ID를 기반으로 삭제하도록 처리 --- .../service/SignOutVolunteerService.java | 30 +++++++++++++++++++ .../usecase/SignOutVolunteerUseCase.java | 8 +++++ 2 files changed, 38 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java create mode 100644 src/main/java/com/somemore/volunteer/usecase/SignOutVolunteerUseCase.java diff --git a/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java b/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java new file mode 100644 index 000000000..be75af84e --- /dev/null +++ b/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java @@ -0,0 +1,30 @@ +package com.somemore.volunteer.service; + +import com.somemore.auth.cookie.CookieUseCase; +import com.somemore.auth.jwt.refresh.manager.RefreshTokenManager; +import com.somemore.volunteer.usecase.SignOutVolunteerUseCase; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional +public class SignOutVolunteerService implements SignOutVolunteerUseCase { + + private final CookieUseCase cookieUseCase; + private final RefreshTokenManager refreshTokenManager; + + @Override + public void signOut( + HttpServletResponse response, + @AuthenticationPrincipal String volunteerId) { + + cookieUseCase.deleteAccessToken(response); + refreshTokenManager.removeRefreshToken(volunteerId); + } +} diff --git a/src/main/java/com/somemore/volunteer/usecase/SignOutVolunteerUseCase.java b/src/main/java/com/somemore/volunteer/usecase/SignOutVolunteerUseCase.java new file mode 100644 index 000000000..b5a236ed6 --- /dev/null +++ b/src/main/java/com/somemore/volunteer/usecase/SignOutVolunteerUseCase.java @@ -0,0 +1,8 @@ +package com.somemore.volunteer.usecase; + +import jakarta.servlet.http.HttpServletResponse; + +public interface SignOutVolunteerUseCase { + + void signOut(HttpServletResponse response, String volunteerId); +} From d1fa733bd07f6c031b8a5e1806b22de82e2ba2b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:42:28 +0900 Subject: [PATCH 08/40] =?UTF-8?q?test(CookieService):=20=EC=95=A1=EC=84=B8?= =?UTF-8?q?=EC=8A=A4=20=ED=86=A0=ED=81=B0=20=EC=84=A4=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C=20=EB=A1=9C=EC=A7=81=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 액세스 토큰 설정 동작 검증 - 토큰 값, HttpOnly, Secure, Path 등 쿠키 속성 확인 - 액세스 토큰 삭제 동작 검증 - 쿠키 값에 SIGNOUT이 포함된 것을 확인 - 쿠키 값이 삭제(Max-Age=0)되었는지 확인 --- .../auth/cookie/CookieServiceTest.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/test/java/com/somemore/auth/cookie/CookieServiceTest.java diff --git a/src/test/java/com/somemore/auth/cookie/CookieServiceTest.java b/src/test/java/com/somemore/auth/cookie/CookieServiceTest.java new file mode 100644 index 000000000..6dbab7163 --- /dev/null +++ b/src/test/java/com/somemore/auth/cookie/CookieServiceTest.java @@ -0,0 +1,44 @@ +package com.somemore.auth.cookie; + +import com.somemore.auth.jwt.domain.TokenType; +import org.junit.jupiter.api.Test; +import org.springframework.mock.web.MockHttpServletResponse; + +import static org.assertj.core.api.Assertions.assertThat; + +class CookieServiceTest { + + private final CookieService cookieService = new CookieService(); + + @Test + void setAccessToken_ShouldSetCookie() { + // Given + MockHttpServletResponse response = new MockHttpServletResponse(); + String accessToken = "test-access-token"; + + // When + cookieService.setAccessToken(response, accessToken); + + // Then + String setCookieHeader = response.getHeader("Set-Cookie"); + assertThat(setCookieHeader).contains("ACCESS=" + accessToken); + assertThat(setCookieHeader).contains("HttpOnly"); + assertThat(setCookieHeader).contains("Secure"); + assertThat(setCookieHeader).contains("Path=/"); + } + + @Test + void deleteAccessToken_ShouldRemoveCookie() { + // Given + MockHttpServletResponse response = new MockHttpServletResponse(); + + // When + cookieService.deleteAccessToken(response); + + // Then + String setCookieHeader = response.getHeader("Set-Cookie"); + assertThat(setCookieHeader).contains("ACCESS=" + TokenType.SIGNOUT.name()); // 빈 값 + assertThat(setCookieHeader).contains("Max-Age=0"); // 삭제 + assertThat(setCookieHeader).contains("Path=/"); + } +} \ No newline at end of file From d5402ba906b178100b51f3578764b3a376c481d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:44:38 +0900 Subject: [PATCH 09/40] =?UTF-8?q?feat(OAuth):=20OAuth=20URL=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OAuth 인증 URL을 동적으로 생성 - 백엔드 Root URL과 OAuth Provider 이름을 조합하여 URL 생성 - /oauth2/authorization/{provider} 형태의 URL 반환 로직 구현 - 환경에 따라 백엔드 Root URL을 설정할 수 있도록 @Value("${app.back-url}") 적용 --- .../service/GenerateOAuthUrlService.java | 32 +++++++++++++++++++ .../usecase/GenerateOAuthUrlUseCase.java | 5 +++ 2 files changed, 37 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/service/GenerateOAuthUrlService.java create mode 100644 src/main/java/com/somemore/volunteer/usecase/GenerateOAuthUrlUseCase.java diff --git a/src/main/java/com/somemore/volunteer/service/GenerateOAuthUrlService.java b/src/main/java/com/somemore/volunteer/service/GenerateOAuthUrlService.java new file mode 100644 index 000000000..8e179e5d4 --- /dev/null +++ b/src/main/java/com/somemore/volunteer/service/GenerateOAuthUrlService.java @@ -0,0 +1,32 @@ +package com.somemore.volunteer.service; + +import com.somemore.volunteer.usecase.GenerateOAuthUrlUseCase; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.util.UriComponentsBuilder; + +@Slf4j +@Service +@RequiredArgsConstructor +public class GenerateOAuthUrlService implements GenerateOAuthUrlUseCase { + + @Value("${app.back-url}") + private String backendRootUrl; + + @Override + public String generateUrl(String oAuthProvider) { + return UriComponentsBuilder.fromHttpUrl(generateBaseUrl()) + .pathSegment(oAuthProvider) + .build() + .toUriString(); + } + + private String generateBaseUrl() { + return UriComponentsBuilder.fromHttpUrl(backendRootUrl) + .path("/oauth2/authorization") + .build() + .toUriString(); + } +} diff --git a/src/main/java/com/somemore/volunteer/usecase/GenerateOAuthUrlUseCase.java b/src/main/java/com/somemore/volunteer/usecase/GenerateOAuthUrlUseCase.java new file mode 100644 index 000000000..1a91f69c8 --- /dev/null +++ b/src/main/java/com/somemore/volunteer/usecase/GenerateOAuthUrlUseCase.java @@ -0,0 +1,5 @@ +package com.somemore.volunteer.usecase; + +public interface GenerateOAuthUrlUseCase { + String generateUrl(String oAuthProvider); +} From dc819bf3f9077bbaf4a2b2e0f36c3468c1b7071d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:46:25 +0900 Subject: [PATCH 10/40] =?UTF-8?q?test(GenerateOAuthUrlService):=20OAuth=20?= =?UTF-8?q?URL=20=EC=83=9D=EC=84=B1=20=EB=A1=9C=EC=A7=81=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OAuth Provider에 따라 올바른 URL이 생성되는지 검증 --- .../service/GenerateOAuthUrlServiceTest.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/test/java/com/somemore/volunteer/service/GenerateOAuthUrlServiceTest.java diff --git a/src/test/java/com/somemore/volunteer/service/GenerateOAuthUrlServiceTest.java b/src/test/java/com/somemore/volunteer/service/GenerateOAuthUrlServiceTest.java new file mode 100644 index 000000000..be5d20140 --- /dev/null +++ b/src/test/java/com/somemore/volunteer/service/GenerateOAuthUrlServiceTest.java @@ -0,0 +1,46 @@ +package com.somemore.volunteer.service; + +import com.somemore.IntegrationTestSupport; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; + +import static org.assertj.core.api.Assertions.assertThat; + +class GenerateOAuthUrlServiceTest extends IntegrationTestSupport { + + @Autowired + private GenerateOAuthUrlService generateOAuthUrlService; + + @Value("${app.back-url}") + private String backendRootUrl; + + + @Test + void generateUrl_ShouldReturnCorrectUrl_ForNaver() { + // Given + String oAuthProvider = "naver"; + + // When + String result = generateOAuthUrlService.generateUrl(oAuthProvider); + + // Then + String expectedUrl = backendRootUrl + "/oauth2/authorization/naver"; + + assertThat(result).isEqualTo(expectedUrl); + } + + @Test + void generateUrl_ShouldReturnCorrectUrl_ForGoogle() { + // Given + String oAuthProvider = "google"; + + // When + String result = generateOAuthUrlService.generateUrl(oAuthProvider); + + // Then + String expectedUrl = backendRootUrl + "/oauth2/authorization/google"; + + assertThat(result).isEqualTo(expectedUrl); + } +} \ No newline at end of file From 3a027a90f035b90a1033034f183abb7decaa6284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:14:21 +0900 Subject: [PATCH 11/40] =?UTF-8?q?test(SignOutVolunteerService):=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20=EB=8F=99=EC=9E=91=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 테스트 후 레디스 delete all - 액세스 토큰 쿠키 삭제 및 리프레시 토큰 제거 검증 - 리프레시 토큰이 없는 경우에도 예외가 발생하지 않는지 확인 - 테스트 시 MockHttpServletResponse를 활용하여 쿠키 동작 검증 - 쿠키 name이 중복으로 저장되는 문제 회피 (트러블슈팅 기록) --- .../auth/jwt/service/JwtServiceTest.java | 12 +++ .../service/SignOutVolunteerServiceTest.java | 93 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java diff --git a/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java b/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java index 509c513ce..600b16250 100644 --- a/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java +++ b/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java @@ -11,10 +11,13 @@ import com.somemore.auth.jwt.validator.JwtValidator; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.transaction.annotation.Transactional; import javax.crypto.SecretKey; import java.time.Instant; @@ -34,6 +37,15 @@ class JwtServiceTest extends IntegrationTestSupport { private SecretKey secretKey; @Autowired private RefreshTokenManager refreshTokenManager; + @Autowired + private RedisTemplate redisTemplate; + + + @AfterEach + void tearDown() { + redisTemplate.keys("*") + .forEach(redisTemplate::delete); + } @DisplayName("토큰이 올바르게 생성된다") @Test diff --git a/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java b/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java new file mode 100644 index 000000000..83bdc06a2 --- /dev/null +++ b/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java @@ -0,0 +1,93 @@ +package com.somemore.volunteer.service; + +import com.somemore.IntegrationTestSupport; +import com.somemore.auth.cookie.CookieUseCase; +import com.somemore.auth.jwt.domain.EncodedToken; +import com.somemore.auth.jwt.domain.TokenType; +import com.somemore.auth.jwt.exception.JwtErrorType; +import com.somemore.auth.jwt.exception.JwtException; +import com.somemore.auth.jwt.filter.UserRole; +import com.somemore.auth.jwt.generator.JwtGenerator; +import com.somemore.auth.jwt.refresh.domain.RefreshToken; +import com.somemore.auth.jwt.refresh.manager.RefreshTokenManager; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.*; + +@Transactional +class SignOutVolunteerServiceTest extends IntegrationTestSupport { + + @Autowired + private SignOutVolunteerService signOutVolunteerService; + @Autowired + private CookieUseCase cookieUseCase; + @Autowired + private RefreshTokenManager refreshTokenManager; + @Autowired + private JwtGenerator jwtGenerator; + @Autowired + private RedisTemplate redisTemplate; + + private MockHttpServletResponse response; + private String volunteerId; + private UserRole role; + + + @BeforeEach + void setUp() { + response = new MockHttpServletResponse(); + volunteerId = "test-volunteer"; + role = UserRole.VOLUNTEER; + } + + @AfterEach + void tearDown() { + redisTemplate.keys("*") + .forEach(redisTemplate::delete); + } + + @Test + @DisplayName("로그아웃 시 액세스 토큰 쿠키를 삭제하고 리프레시 토큰을 제거해야 한다.") + void signOutDeletesTokens() { + // Given + EncodedToken accessToken = jwtGenerator.generateToken(volunteerId, role.name(), TokenType.ACCESS); + + RefreshToken refreshToken = new RefreshToken( + volunteerId, + accessToken, + jwtGenerator.generateToken(volunteerId, role.name(), TokenType.REFRESH)); + + refreshTokenManager.save(refreshToken); + cookieUseCase.setAccessToken(response, accessToken.value()); + + // When + signOutVolunteerService.signOut(response, volunteerId); + + // Then + assertThatThrownBy(() -> refreshTokenManager.findRefreshToken(accessToken)) + .isInstanceOf(JwtException.class) + .hasMessage(JwtErrorType.EXPIRED_TOKEN.getMessage()); + + assertThat(Arrays.toString(response.getCookies())).contains(TokenType.SIGNOUT.name()); + } + + @Test + @DisplayName("로그아웃 시 리프레시 토큰이 없어도 예외가 발생하지 않는다.") + void signOutWithoutRefreshToken() { + // When + signOutVolunteerService.signOut(response, volunteerId); + + // Then + assertThatNoException().isThrownBy(() -> signOutVolunteerService.signOut(response, volunteerId)); + assertThat(Arrays.toString(response.getCookies())).contains(TokenType.SIGNOUT.name()); + } +} \ No newline at end of file From 535e62ac43dc709bd8ed678fe7a1dc79f3a5d323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:18:35 +0900 Subject: [PATCH 12/40] =?UTF-8?q?feat(application.yml):=20front/back=20url?= =?UTF-8?q?=20=EB=B3=80=EC=88=98=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b4eb6c368..0484bb2aa 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,6 @@ app: - front-url: "http://localhost:3000" - back-url: "http://localhost:8080" + front-url: ${FRONT_URL} + back-url: ${BACK_URL} spring: datasource: From 0e9e6674e559050b3e76f326972cfd2e6c872a29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:20:17 +0900 Subject: [PATCH 13/40] =?UTF-8?q?feat(VolunteerSignController):=20OAuth=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=95=84=EC=9B=83=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OAuth 로그인 URL 생성 API 추가 - [POST] /api/volunteer/sign-in/oauth/{oauthProvider} - 지원되는 OAuth 제공자에 따라 리다이렉트 URL 생성 및 반환 - 지원되지 않는 OAuth 제공자는 `BadRequestException` 처리 - 로그아웃 API 추가 - [POST] /api/volunteer/sign-out - 액세스 토큰 쿠키 삭제 및 리프레시 토큰 제거 - 성공 시 표준 응답 형식(`ApiResponse.ok`)으로 메시지 반환 - GenerateOAuthUrlUseCase를 사용해 OAuth URL 동적 생성 - SignOutVolunteerUseCase를 사용해 로그아웃 로직 처리 --- .../query/VolunteerSignController.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/controller/query/VolunteerSignController.java diff --git a/src/main/java/com/somemore/volunteer/controller/query/VolunteerSignController.java b/src/main/java/com/somemore/volunteer/controller/query/VolunteerSignController.java new file mode 100644 index 000000000..4e7660483 --- /dev/null +++ b/src/main/java/com/somemore/volunteer/controller/query/VolunteerSignController.java @@ -0,0 +1,53 @@ +package com.somemore.volunteer.controller.query; + +import com.somemore.global.common.response.ApiResponse; +import com.somemore.global.exception.BadRequestException; +import com.somemore.volunteer.usecase.SignOutVolunteerUseCase; +import com.somemore.volunteer.usecase.GenerateOAuthUrlUseCase; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.view.RedirectView; + +@RestController +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/api/volunteer") +@Tag(name = "Volunteer OAuth API", description = "Handles Volunteer OAuth Sign-in, Sign-out") +public class VolunteerSignController { + + private final GenerateOAuthUrlUseCase generateOAuthUrlUseCase; + private final SignOutVolunteerUseCase signOutVolunteerUseCase; + + @PostMapping("/sign-in/oauth/{oauthProvider}") + public RedirectView signIn( + @Parameter(name = "oauthProvider", description = "OAuth 제공자 선택", example = "naver", required = true, schema = @Schema(allowableValues = {"naver"})) + @PathVariable("oauthProvider") String oauthProvider) { + + String redirectUrl = switch (oauthProvider.toLowerCase()) { + case "naver" -> generateOAuthUrlUseCase.generateUrl(oauthProvider); + + default -> throw new BadRequestException("지원되지 않는 OAuth 제공자: " + oauthProvider); + }; + + return new RedirectView(redirectUrl); + } + + @PostMapping("/sign-out") + public ApiResponse signOut( + HttpServletResponse response, + @AuthenticationPrincipal String userId) { + + signOutVolunteerUseCase.signOut(response, userId); + + return ApiResponse.ok("로그아웃되었습니다"); + } +} From ec455061dd30290a60c7a8fc4f666e2858cf72a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:23:03 +0900 Subject: [PATCH 14/40] =?UTF-8?q?feat(application.yml):=20naver-redirect-u?= =?UTF-8?q?ri=20=EB=B3=80=EC=88=98=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0484bb2aa..be25b9b53 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -32,7 +32,7 @@ spring: naver: client-id: ${NAVER_CLIENT_ID} client-secret: ${NAVER_CLIENT_SECRET} - redirect-uri: "http://localhost:8080/login/oauth2/code/naver" + redirect-uri: ${NAVER_REDIRECT_URL} authorization-grant-type: authorization_code scope: ${NAVER_SCOPE} From 317e6acc37815b2643f0b4fcdbff72d829f4c251 Mon Sep 17 00:00:00 2001 From: seojin Yoon <90759319+7zrv@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:50:27 +0900 Subject: [PATCH 15/40] =?UTF-8?q?CICD/=20=ED=99=98=EA=B2=BD=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EC=B6=94=EA=B0=80=20(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * cicd: 환경변수 추가 - OAuth 연동을 위한 환경변수 추가 --- .github/workflows/CD.yml | 3 +++ .github/workflows/CI.yml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/CD.yml b/.github/workflows/CD.yml index 36191dc35..dabaf0779 100644 --- a/.github/workflows/CD.yml +++ b/.github/workflows/CD.yml @@ -23,7 +23,10 @@ jobs: NAVER_CLIENT_ID: ${{ secrets.NAVER_CLIENT_ID }} NAVER_CLIENT_SECRET: ${{ secrets.NAVER_CLIENT_SECRET }} NAVER_SCOPE: ${{ secrets.NAVER_SCOPE }} + NAVER_REDIRECT_URL: ${{secrets.NAVER_REDIRECT_URL}} JWT_SECRET: ${{ secrets.JWT_SECRET }} + FRONT_URL: ${{secrets.FRONT_URL}} + BACK_URL: ${{secrets.BACK_URL}} steps: - name: Github Repository 파일 불러오기 diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e6a68cf04..26939d2b9 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -31,7 +31,10 @@ jobs: NAVER_CLIENT_ID: ${{ secrets.NAVER_CLIENT_ID }} NAVER_CLIENT_SECRET: ${{ secrets.NAVER_CLIENT_SECRET }} NAVER_SCOPE: ${{ secrets.NAVER_SCOPE }} + NAVER_REDIRECT_URL: ${{secrets.NAVER_REDIRECT_URL}} JWT_SECRET: ${{ secrets.JWT_SECRET }} + FRONT_URL: ${{secrets.FRONT_URL}} + BACK_URL: ${{secrets.BACK_URL}} steps: From 195e39cec5eeb0348cf3b2ba38d2e124536e6d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:23:50 +0900 Subject: [PATCH 16/40] =?UTF-8?q?refactor(package):=20C/Q=20=EA=B5=AC?= =?UTF-8?q?=EB=B6=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/{command => }/GenerateTokensOnLoginService.java | 4 ++-- .../usecase/{command => }/GenerateTokensOnLoginUseCase.java | 2 +- .../auth/oauth/handler/success/CustomOAuthSuccessHandler.java | 2 +- .../controller/{query => }/VolunteerSignController.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename src/main/java/com/somemore/auth/jwt/service/{command => }/GenerateTokensOnLoginService.java (93%) rename src/main/java/com/somemore/auth/jwt/usecase/{command => }/GenerateTokensOnLoginUseCase.java (80%) rename src/main/java/com/somemore/volunteer/controller/{query => }/VolunteerSignController.java (97%) diff --git a/src/main/java/com/somemore/auth/jwt/service/command/GenerateTokensOnLoginService.java b/src/main/java/com/somemore/auth/jwt/service/GenerateTokensOnLoginService.java similarity index 93% rename from src/main/java/com/somemore/auth/jwt/service/command/GenerateTokensOnLoginService.java rename to src/main/java/com/somemore/auth/jwt/service/GenerateTokensOnLoginService.java index 4bbbbd74d..a0cd82ee3 100644 --- a/src/main/java/com/somemore/auth/jwt/service/command/GenerateTokensOnLoginService.java +++ b/src/main/java/com/somemore/auth/jwt/service/GenerateTokensOnLoginService.java @@ -1,4 +1,4 @@ -package com.somemore.auth.jwt.service.command; +package com.somemore.auth.jwt.service; import com.somemore.auth.jwt.domain.UserRole; import com.somemore.auth.jwt.domain.EncodedToken; @@ -6,7 +6,7 @@ import com.somemore.auth.jwt.generator.JwtGenerator; import com.somemore.auth.jwt.refresh.domain.RefreshToken; import com.somemore.auth.jwt.refresh.manager.RefreshTokenManager; -import com.somemore.auth.jwt.usecase.command.GenerateTokensOnLoginUseCase; +import com.somemore.auth.jwt.usecase.GenerateTokensOnLoginUseCase; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/somemore/auth/jwt/usecase/command/GenerateTokensOnLoginUseCase.java b/src/main/java/com/somemore/auth/jwt/usecase/GenerateTokensOnLoginUseCase.java similarity index 80% rename from src/main/java/com/somemore/auth/jwt/usecase/command/GenerateTokensOnLoginUseCase.java rename to src/main/java/com/somemore/auth/jwt/usecase/GenerateTokensOnLoginUseCase.java index 912f205f0..0c48a4cce 100644 --- a/src/main/java/com/somemore/auth/jwt/usecase/command/GenerateTokensOnLoginUseCase.java +++ b/src/main/java/com/somemore/auth/jwt/usecase/GenerateTokensOnLoginUseCase.java @@ -1,4 +1,4 @@ -package com.somemore.auth.jwt.usecase.command; +package com.somemore.auth.jwt.usecase; import com.somemore.auth.jwt.domain.EncodedToken; diff --git a/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java b/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java index d26edadfd..b090b8db4 100644 --- a/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java +++ b/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java @@ -2,7 +2,7 @@ import com.somemore.auth.cookie.CookieUseCase; import com.somemore.auth.jwt.domain.EncodedToken; -import com.somemore.auth.jwt.usecase.command.GenerateTokensOnLoginUseCase; +import com.somemore.auth.jwt.usecase.GenerateTokensOnLoginUseCase; import com.somemore.auth.oauth.OAuthProvider; import com.somemore.auth.oauth.naver.service.query.ProcessNaverOAuthUserService; import com.somemore.auth.redirect.RedirectUseCase; diff --git a/src/main/java/com/somemore/volunteer/controller/query/VolunteerSignController.java b/src/main/java/com/somemore/volunteer/controller/VolunteerSignController.java similarity index 97% rename from src/main/java/com/somemore/volunteer/controller/query/VolunteerSignController.java rename to src/main/java/com/somemore/volunteer/controller/VolunteerSignController.java index 4e7660483..9d7f3445a 100644 --- a/src/main/java/com/somemore/volunteer/controller/query/VolunteerSignController.java +++ b/src/main/java/com/somemore/volunteer/controller/VolunteerSignController.java @@ -1,4 +1,4 @@ -package com.somemore.volunteer.controller.query; +package com.somemore.volunteer.controller; import com.somemore.global.common.response.ApiResponse; import com.somemore.global.exception.BadRequestException; From 300c3018ec9fd3800ede199857ead459a5c72a48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:44:30 +0900 Subject: [PATCH 17/40] =?UTF-8?q?feat(application.yml):=20UTF-8,=20KR=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 11 +++++++++++ src/test/resources/application-test.yml | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index be25b9b53..73394baa0 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -43,6 +43,10 @@ spring: user-info-uri: https://openapi.naver.com/v1/nid/me user-name-attribute: response # 네이버 API가 사용자 정보를 "response" 객체 안에 넣어 반환 + web: + locale: ko_KR + locale-resolver: fixed + #swagger springdoc: swagger-ui: @@ -68,3 +72,10 @@ jwt: logging: level: org.springframework.security: DEBUG + +server: + servlet: + encoding: + charset: UTF-8 + enabled: true + force: true diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index a53533e3e..fab7dd965 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -31,5 +31,16 @@ spring: port: 6379 password: # 테스트에서는 비밀번호 없이 연결 + web: + locale: ko_KR + locale-resolver: fixed + jwt: secret: 63bf2c80266cd25072e53b3482e318c30d1cd18d8c98d0f5d278530a94fe28d9fbbec531e5ccb58c725c125738182357786b71f43a7172c5d0c94a17f0da44f2 # 테스트용 JWT 시크릿 키 + +server: + servlet: + encoding: + charset: UTF-8 + enabled: true + force: true From 10cd7901d7134361003ce6ff480d408fd37af057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:45:00 +0900 Subject: [PATCH 18/40] =?UTF-8?q?test(VolunteerSignController):=20OAuth=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 유효한 OAuth 제공자로 로그인 URL 생성 테스트 추가 - 지원되지 않는 OAuth 제공자에 대해 400 에러 반환 테스트 추가 - 로그아웃 요청 시 성공 메시지 반환 테스트 추가 --- .../VolunteerSignControllerTest.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/test/java/com/somemore/volunteer/controller/VolunteerSignControllerTest.java diff --git a/src/test/java/com/somemore/volunteer/controller/VolunteerSignControllerTest.java b/src/test/java/com/somemore/volunteer/controller/VolunteerSignControllerTest.java new file mode 100644 index 000000000..1af24e071 --- /dev/null +++ b/src/test/java/com/somemore/volunteer/controller/VolunteerSignControllerTest.java @@ -0,0 +1,66 @@ +package com.somemore.volunteer.controller; + +import com.somemore.IntegrationTestSupport; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.web.servlet.MockMvc; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +class VolunteerSignControllerTest extends IntegrationTestSupport { + + @Autowired + private MockMvc mockMvc; + + @Test + @DisplayName("유효한 OAuth 제공자로 로그인 URL을 생성한다.") + void signInWithValidProvider() throws Exception { + // Given + String oauthProvider = "naver"; + + // When + // Then + mockMvc.perform(post("/api/volunteer/sign-in/oauth/{oauthProvider}", oauthProvider)) + .andExpect(status().is3xxRedirection()) + .andExpect(result -> { + MockHttpServletResponse response = result.getResponse(); + String redirectedUrl = response.getRedirectedUrl(); + assertThat(redirectedUrl).isNotNull(); + assertThat(redirectedUrl).contains("oauth2/authorization/naver"); + }); + } + + @Test + @DisplayName("지원되지 않는 OAuth 제공자로 로그인 시 400 에러를 반환한다.") + void signInWithInvalidProvider() throws Exception { + // Given + String invalidProvider = "unsupported-provider"; + + // When + // Then + mockMvc.perform(post("/api/volunteer/sign-in/oauth/{oauthProvider}", invalidProvider)) + .andExpect(status().isBadRequest()); + } + + @Test + @DisplayName("로그아웃 요청 시 성공 메시지를 반환한다.") + void signOut() throws Exception { + // When + // Then + mockMvc.perform(post("/api/volunteer/sign-out")) + .andExpect(status().isOk()) + .andExpect(result -> { + MockHttpServletResponse response = result.getResponse(); + String responseBody = response.getContentAsString(); + assertThat(responseBody).contains("로그아웃되었습니다"); + }); + } +} \ No newline at end of file From 6de2b860878ad453b00a109e1e777a818729be64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:46:19 +0900 Subject: [PATCH 19/40] =?UTF-8?q?test(@Transactional):=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=ED=8A=B8=EB=9E=9C=EC=9E=AD?= =?UTF-8?q?=EC=85=98=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java | 1 - .../somemore/volunteer/service/SignOutVolunteerServiceTest.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java b/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java index 600b16250..0519bcd3a 100644 --- a/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java +++ b/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java @@ -17,7 +17,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.transaction.annotation.Transactional; import javax.crypto.SecretKey; import java.time.Instant; diff --git a/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java b/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java index 83bdc06a2..9a442c7dc 100644 --- a/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java +++ b/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java @@ -23,7 +23,6 @@ import static org.assertj.core.api.Assertions.*; -@Transactional class SignOutVolunteerServiceTest extends IntegrationTestSupport { @Autowired From 851a2c20c76d1426faf1a660b7f019fc2fe93234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:46:46 +0900 Subject: [PATCH 20/40] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/somemore/volunteer/service/SignOutVolunteerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java b/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java index be75af84e..b2605039f 100644 --- a/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java +++ b/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java @@ -22,7 +22,7 @@ public class SignOutVolunteerService implements SignOutVolunteerUseCase { @Override public void signOut( HttpServletResponse response, - @AuthenticationPrincipal String volunteerId) { + String volunteerId) { cookieUseCase.deleteAccessToken(response); refreshTokenManager.removeRefreshToken(volunteerId); From 1b21234d03ca54133c110f720165a8a1a1117178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:15:17 +0900 Subject: [PATCH 21/40] =?UTF-8?q?refactor(package):=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/somemore/auth/{ => jwt/filter}/UserRole.java | 2 +- .../volunteer/service/{query => }/FindVolunteerIdService.java | 4 ++-- .../service/{command => }/RegisterVolunteerService.java | 4 ++-- .../volunteer/usecase/{query => }/FindVolunteerIdUseCase.java | 2 +- .../usecase/{command => }/RegisterVolunteerUseCase.java | 2 +- .../service/{query => }/FindVolunteerIdServiceTest.java | 2 +- .../service/{command => }/RegisterVolunteerServiceTest.java | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) rename src/main/java/com/somemore/auth/{ => jwt/filter}/UserRole.java (62%) rename src/main/java/com/somemore/volunteer/service/{query => }/FindVolunteerIdService.java (91%) rename src/main/java/com/somemore/volunteer/service/{command => }/RegisterVolunteerService.java (90%) rename src/main/java/com/somemore/volunteer/usecase/{query => }/FindVolunteerIdUseCase.java (87%) rename src/main/java/com/somemore/volunteer/usecase/{command => }/RegisterVolunteerUseCase.java (78%) rename src/test/java/com/somemore/volunteer/service/{query => }/FindVolunteerIdServiceTest.java (98%) rename src/test/java/com/somemore/volunteer/service/{command => }/RegisterVolunteerServiceTest.java (98%) diff --git a/src/main/java/com/somemore/auth/UserRole.java b/src/main/java/com/somemore/auth/jwt/filter/UserRole.java similarity index 62% rename from src/main/java/com/somemore/auth/UserRole.java rename to src/main/java/com/somemore/auth/jwt/filter/UserRole.java index 0e4d9ed8e..857ee5c8a 100644 --- a/src/main/java/com/somemore/auth/UserRole.java +++ b/src/main/java/com/somemore/auth/jwt/filter/UserRole.java @@ -1,4 +1,4 @@ -package com.somemore.auth; +package com.somemore.auth.jwt.filter; public enum UserRole { VOLUNTEER, diff --git a/src/main/java/com/somemore/volunteer/service/query/FindVolunteerIdService.java b/src/main/java/com/somemore/volunteer/service/FindVolunteerIdService.java similarity index 91% rename from src/main/java/com/somemore/volunteer/service/query/FindVolunteerIdService.java rename to src/main/java/com/somemore/volunteer/service/FindVolunteerIdService.java index 970a643d4..7825aa367 100644 --- a/src/main/java/com/somemore/volunteer/service/query/FindVolunteerIdService.java +++ b/src/main/java/com/somemore/volunteer/service/FindVolunteerIdService.java @@ -1,9 +1,9 @@ -package com.somemore.volunteer.service.query; +package com.somemore.volunteer.service; import com.somemore.volunteer.domain.Volunteer; import com.somemore.volunteer.dto.response.VolunteerForCommunityResponseDto; import com.somemore.volunteer.repository.VolunteerRepository; -import com.somemore.volunteer.usecase.query.FindVolunteerIdUseCase; +import com.somemore.volunteer.usecase.FindVolunteerIdUseCase; import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/src/main/java/com/somemore/volunteer/service/command/RegisterVolunteerService.java b/src/main/java/com/somemore/volunteer/service/RegisterVolunteerService.java similarity index 90% rename from src/main/java/com/somemore/volunteer/service/command/RegisterVolunteerService.java rename to src/main/java/com/somemore/volunteer/service/RegisterVolunteerService.java index 8ea14dea0..8feb930c1 100644 --- a/src/main/java/com/somemore/volunteer/service/command/RegisterVolunteerService.java +++ b/src/main/java/com/somemore/volunteer/service/RegisterVolunteerService.java @@ -1,11 +1,11 @@ -package com.somemore.volunteer.service.command; +package com.somemore.volunteer.service; import com.somemore.volunteer.domain.Volunteer; import com.somemore.volunteer.domain.VolunteerDetail; import com.somemore.volunteer.dto.request.VolunteerRegisterRequestDto; import com.somemore.volunteer.repository.VolunteerDetailRepository; import com.somemore.volunteer.repository.VolunteerRepository; -import com.somemore.volunteer.usecase.command.RegisterVolunteerUseCase; +import com.somemore.volunteer.usecase.RegisterVolunteerUseCase; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/somemore/volunteer/usecase/query/FindVolunteerIdUseCase.java b/src/main/java/com/somemore/volunteer/usecase/FindVolunteerIdUseCase.java similarity index 87% rename from src/main/java/com/somemore/volunteer/usecase/query/FindVolunteerIdUseCase.java rename to src/main/java/com/somemore/volunteer/usecase/FindVolunteerIdUseCase.java index cf1775afb..e23eaa20f 100644 --- a/src/main/java/com/somemore/volunteer/usecase/query/FindVolunteerIdUseCase.java +++ b/src/main/java/com/somemore/volunteer/usecase/FindVolunteerIdUseCase.java @@ -1,4 +1,4 @@ -package com.somemore.volunteer.usecase.query; +package com.somemore.volunteer.usecase; import com.somemore.volunteer.dto.response.VolunteerForCommunityResponseDto; diff --git a/src/main/java/com/somemore/volunteer/usecase/command/RegisterVolunteerUseCase.java b/src/main/java/com/somemore/volunteer/usecase/RegisterVolunteerUseCase.java similarity index 78% rename from src/main/java/com/somemore/volunteer/usecase/command/RegisterVolunteerUseCase.java rename to src/main/java/com/somemore/volunteer/usecase/RegisterVolunteerUseCase.java index 45e06dcf4..0e90d980d 100644 --- a/src/main/java/com/somemore/volunteer/usecase/command/RegisterVolunteerUseCase.java +++ b/src/main/java/com/somemore/volunteer/usecase/RegisterVolunteerUseCase.java @@ -1,4 +1,4 @@ -package com.somemore.volunteer.usecase.command; +package com.somemore.volunteer.usecase; import com.somemore.volunteer.dto.request.VolunteerRegisterRequestDto; diff --git a/src/test/java/com/somemore/volunteer/service/query/FindVolunteerIdServiceTest.java b/src/test/java/com/somemore/volunteer/service/FindVolunteerIdServiceTest.java similarity index 98% rename from src/test/java/com/somemore/volunteer/service/query/FindVolunteerIdServiceTest.java rename to src/test/java/com/somemore/volunteer/service/FindVolunteerIdServiceTest.java index 4bd0ec03b..831348722 100644 --- a/src/test/java/com/somemore/volunteer/service/query/FindVolunteerIdServiceTest.java +++ b/src/test/java/com/somemore/volunteer/service/FindVolunteerIdServiceTest.java @@ -1,4 +1,4 @@ -package com.somemore.volunteer.service.query; +package com.somemore.volunteer.service; import com.somemore.IntegrationTestSupport; import com.somemore.auth.oauth.OAuthProvider; diff --git a/src/test/java/com/somemore/volunteer/service/command/RegisterVolunteerServiceTest.java b/src/test/java/com/somemore/volunteer/service/RegisterVolunteerServiceTest.java similarity index 98% rename from src/test/java/com/somemore/volunteer/service/command/RegisterVolunteerServiceTest.java rename to src/test/java/com/somemore/volunteer/service/RegisterVolunteerServiceTest.java index 8454a470d..543718d3a 100644 --- a/src/test/java/com/somemore/volunteer/service/command/RegisterVolunteerServiceTest.java +++ b/src/test/java/com/somemore/volunteer/service/RegisterVolunteerServiceTest.java @@ -1,4 +1,4 @@ -package com.somemore.volunteer.service.command; +package com.somemore.volunteer.service; import com.somemore.IntegrationTestSupport; import com.somemore.auth.oauth.OAuthProvider; From d21e31fe0f0ce5b9c87cea3cd65611d0a77cdc6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:16:05 +0900 Subject: [PATCH 22/40] =?UTF-8?q?style(ApiResponse):=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EA=B0=9C=ED=96=89=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/somemore/global/common/response/ApiResponse.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/com/somemore/global/common/response/ApiResponse.java b/src/main/java/com/somemore/global/common/response/ApiResponse.java index 3b2b32ef5..ebdd60c3a 100644 --- a/src/main/java/com/somemore/global/common/response/ApiResponse.java +++ b/src/main/java/com/somemore/global/common/response/ApiResponse.java @@ -12,26 +12,20 @@ public class ApiResponse { private T data; public static ApiResponse ok(int status, T data, String message) { - return new ApiResponse<>(status, message, data); } public static ApiResponse ok(String message) { - return new ApiResponse<>(200, message, ""); } public static ApiResponse error(int code, String message) { - return new ApiResponse<>(code, message, ""); } - - public ApiResponse(int code, String message, T data) { this.code = code; this.message = message; this.data = data; } - } From 5bbb1fa4199a51fd785e8524771f753281d2b89b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:17:01 +0900 Subject: [PATCH 23/40] =?UTF-8?q?feat(application.yml):=20front,=20back=20?= =?UTF-8?q?root=20url=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 7 ++++--- src/test/resources/application-test.yml | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e6943650b..b4eb6c368 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,3 +1,7 @@ +app: + front-url: "http://localhost:3000" + back-url: "http://localhost:8080" + spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver @@ -58,9 +62,6 @@ springdoc: paths-to-match: - /api/** -frontend: - url: localhost - jwt: secret: ${JWT_SECRET} diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index a80bba3cc..a53533e3e 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -1,3 +1,7 @@ +app: + front-url: "http://localhost:3000" + back-url: "http://localhost:8080" + spring: config: activate: @@ -27,9 +31,5 @@ spring: port: 6379 password: # 테스트에서는 비밀번호 없이 연결 -frontend: - url: http://localhost:3000 # 테스트용 프론트엔드 주소 - jwt: secret: 63bf2c80266cd25072e53b3482e318c30d1cd18d8c98d0f5d278530a94fe28d9fbbec531e5ccb58c725c125738182357786b71f43a7172c5d0c94a17f0da44f2 # 테스트용 JWT 시크릿 키 - From 72d5bb1387710d8b59db54ffa97fbdc8a75d1dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:18:29 +0900 Subject: [PATCH 24/40] =?UTF-8?q?refactor(package):=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/somemore/auth/jwt/filter/JwtAuthFilter.java | 1 - .../oauth/naver/service/command/NaverOAuth2UserInfoService.java | 2 +- .../somemore/community/service/CommunityBoardQueryService.java | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/somemore/auth/jwt/filter/JwtAuthFilter.java b/src/main/java/com/somemore/auth/jwt/filter/JwtAuthFilter.java index 3f9a2c677..08ced03a6 100644 --- a/src/main/java/com/somemore/auth/jwt/filter/JwtAuthFilter.java +++ b/src/main/java/com/somemore/auth/jwt/filter/JwtAuthFilter.java @@ -1,6 +1,5 @@ package com.somemore.auth.jwt.filter; -import com.somemore.auth.UserRole; import com.somemore.auth.jwt.domain.EncodedToken; import com.somemore.auth.jwt.exception.JwtErrorType; import com.somemore.auth.jwt.exception.JwtException; diff --git a/src/main/java/com/somemore/auth/oauth/naver/service/command/NaverOAuth2UserInfoService.java b/src/main/java/com/somemore/auth/oauth/naver/service/command/NaverOAuth2UserInfoService.java index 496dcf1f3..cf6f92d02 100644 --- a/src/main/java/com/somemore/auth/oauth/naver/service/command/NaverOAuth2UserInfoService.java +++ b/src/main/java/com/somemore/auth/oauth/naver/service/command/NaverOAuth2UserInfoService.java @@ -4,7 +4,7 @@ import com.somemore.auth.oauth.naver.usecase.query.CheckNaverUserUseCase; import com.somemore.auth.oauth.naver.usecase.command.RegisterNaverUserUseCase; import com.somemore.auth.oauth.naver.util.OAuthResponseConverter; -import com.somemore.volunteer.usecase.command.RegisterVolunteerUseCase; +import com.somemore.volunteer.usecase.RegisterVolunteerUseCase; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.oauth2.core.user.OAuth2User; diff --git a/src/main/java/com/somemore/community/service/CommunityBoardQueryService.java b/src/main/java/com/somemore/community/service/CommunityBoardQueryService.java index 00aeb74b8..aca97f3f7 100644 --- a/src/main/java/com/somemore/community/service/CommunityBoardQueryService.java +++ b/src/main/java/com/somemore/community/service/CommunityBoardQueryService.java @@ -9,7 +9,7 @@ import com.somemore.community.usecase.CommunityBoardQueryUseCase; import com.somemore.global.exception.BadRequestException; import com.somemore.volunteer.dto.response.VolunteerForCommunityResponseDto; -import com.somemore.volunteer.usecase.query.FindVolunteerIdUseCase; +import com.somemore.volunteer.usecase.FindVolunteerIdUseCase; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; From 7ffd3e20afd0001320570bb642e169a77c63fd3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:20:31 +0900 Subject: [PATCH 25/40] =?UTF-8?q?feat(cookie):=20=EC=BF=A0=ED=82=A4=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C(=EB=8D=AE=EC=96=B4=EC=94=8C=EC=9A=B0?= =?UTF-8?q?=EA=B8=B0),=20=ED=86=A0=ED=81=B0=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80(SIGNOUT),=20=ED=8C=8C=EC=9D=BC=EB=AA=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../somemore/auth/cookie/CookieService.java | 36 +++++++++++++++++++ .../somemore/auth/cookie/CookieUseCase.java | 9 +++++ .../auth/cookie/SetCookieService.java | 30 ---------------- .../auth/cookie/SetCookieUseCase.java | 8 ----- .../somemore/auth/jwt/domain/TokenType.java | 3 +- .../somemore/auth/jwt/service/JwtService.java | 6 ++-- .../success/CustomOAuthSuccessHandler.java | 11 +++--- 7 files changed, 55 insertions(+), 48 deletions(-) create mode 100644 src/main/java/com/somemore/auth/cookie/CookieService.java create mode 100644 src/main/java/com/somemore/auth/cookie/CookieUseCase.java delete mode 100644 src/main/java/com/somemore/auth/cookie/SetCookieService.java delete mode 100644 src/main/java/com/somemore/auth/cookie/SetCookieUseCase.java diff --git a/src/main/java/com/somemore/auth/cookie/CookieService.java b/src/main/java/com/somemore/auth/cookie/CookieService.java new file mode 100644 index 000000000..f68080cca --- /dev/null +++ b/src/main/java/com/somemore/auth/cookie/CookieService.java @@ -0,0 +1,36 @@ +package com.somemore.auth.cookie; + +import com.somemore.auth.jwt.domain.TokenType; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseCookie; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +@Slf4j +public class CookieService implements CookieUseCase { + + @Override + public void setAccessToken(HttpServletResponse response, String value) { + ResponseCookie cookie = generateCookie(TokenType.ACCESS, value); + response.addHeader("Set-Cookie", cookie.toString()); + } + + @Override + public void deleteAccessToken(HttpServletResponse response) { + ResponseCookie cookie = generateCookie(TokenType.SIGNOUT, TokenType.SIGNOUT.name()); + response.addHeader("Set-Cookie", cookie.toString()); + } + + private static ResponseCookie generateCookie(TokenType tokenType, String value) { + return ResponseCookie.from(TokenType.ACCESS.name(), value) // 덮어쓰기 위해서 고정 값 + .httpOnly(true) + .secure(true) + .path("/") + .maxAge(tokenType.getPeriodInSeconds()) + .sameSite("Lax") + .build(); + } +} diff --git a/src/main/java/com/somemore/auth/cookie/CookieUseCase.java b/src/main/java/com/somemore/auth/cookie/CookieUseCase.java new file mode 100644 index 000000000..40f5e04f5 --- /dev/null +++ b/src/main/java/com/somemore/auth/cookie/CookieUseCase.java @@ -0,0 +1,9 @@ +package com.somemore.auth.cookie; + +import jakarta.servlet.http.HttpServletResponse; + +public interface CookieUseCase { + void setAccessToken(HttpServletResponse response, String value); + + void deleteAccessToken(HttpServletResponse response); +} diff --git a/src/main/java/com/somemore/auth/cookie/SetCookieService.java b/src/main/java/com/somemore/auth/cookie/SetCookieService.java deleted file mode 100644 index f728dac31..000000000 --- a/src/main/java/com/somemore/auth/cookie/SetCookieService.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.somemore.auth.cookie; - -import com.somemore.auth.jwt.domain.TokenType; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseCookie; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -@Slf4j -public class SetCookieService implements SetCookieUseCase { - - @Override - public void setToken(HttpServletResponse response, String value, TokenType tokenType) { - ResponseCookie cookie = generateCookie(tokenType.name(), value, tokenType.getPeriodInSeconds()); - response.addHeader("Set-Cookie", cookie.toString()); - } - - private static ResponseCookie generateCookie(String name, String value, int time) { - return ResponseCookie.from(name, value) - .httpOnly(true) - .secure(true) - .path("/") - .maxAge(time) - .sameSite("Lax") - .build(); - } -} diff --git a/src/main/java/com/somemore/auth/cookie/SetCookieUseCase.java b/src/main/java/com/somemore/auth/cookie/SetCookieUseCase.java deleted file mode 100644 index 148e2e013..000000000 --- a/src/main/java/com/somemore/auth/cookie/SetCookieUseCase.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.somemore.auth.cookie; - -import com.somemore.auth.jwt.domain.TokenType; -import jakarta.servlet.http.HttpServletResponse; - -public interface SetCookieUseCase { - void setToken(HttpServletResponse response, String value, TokenType tokenType); -} diff --git a/src/main/java/com/somemore/auth/jwt/domain/TokenType.java b/src/main/java/com/somemore/auth/jwt/domain/TokenType.java index 55a4e88f4..e6ea4638e 100644 --- a/src/main/java/com/somemore/auth/jwt/domain/TokenType.java +++ b/src/main/java/com/somemore/auth/jwt/domain/TokenType.java @@ -5,7 +5,8 @@ @Getter public enum TokenType { ACCESS(1000 * 60 * 30), - REFRESH(1000 * 60 * 60 * 24 * 7); + REFRESH(1000 * 60 * 60 * 24 * 7), + SIGNOUT(0); private final int period; diff --git a/src/main/java/com/somemore/auth/jwt/service/JwtService.java b/src/main/java/com/somemore/auth/jwt/service/JwtService.java index dd84f0b20..6bf18ba98 100644 --- a/src/main/java/com/somemore/auth/jwt/service/JwtService.java +++ b/src/main/java/com/somemore/auth/jwt/service/JwtService.java @@ -1,6 +1,6 @@ package com.somemore.auth.jwt.service; -import com.somemore.auth.cookie.SetCookieUseCase; +import com.somemore.auth.cookie.CookieUseCase; import com.somemore.auth.jwt.domain.EncodedToken; import com.somemore.auth.jwt.domain.TokenType; import com.somemore.auth.jwt.exception.JwtErrorType; @@ -24,7 +24,7 @@ public class JwtService implements JwtUseCase { private final JwtParser jwtParser; private final JwtValidator jwtValidator; private final JwtRefresher jwtRefresher; - private final SetCookieUseCase setCookieUseCase; + private final CookieUseCase cookieUseCase; @Override public EncodedToken generateToken(String userId, String role, TokenType tokenType) { @@ -48,7 +48,7 @@ public Claims getClaims(EncodedToken token) { private void handleJwtExpiredException(JwtException e, EncodedToken accessToken, HttpServletResponse response) { if (e.getErrorType() == JwtErrorType.EXPIRED_TOKEN) { EncodedToken refreshedToken = jwtRefresher.refreshAccessToken(accessToken); - setCookieUseCase.setToken(response, refreshedToken.value(), TokenType.ACCESS); + cookieUseCase.setAccessToken(response, refreshedToken.value()); return; } throw e; diff --git a/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java b/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java index 5f389367b..d26edadfd 100644 --- a/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java +++ b/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java @@ -1,13 +1,12 @@ package com.somemore.auth.oauth.handler.success; -import com.somemore.auth.cookie.SetCookieUseCase; +import com.somemore.auth.cookie.CookieUseCase; import com.somemore.auth.jwt.domain.EncodedToken; -import com.somemore.auth.jwt.domain.TokenType; import com.somemore.auth.jwt.usecase.command.GenerateTokensOnLoginUseCase; import com.somemore.auth.oauth.OAuthProvider; import com.somemore.auth.oauth.naver.service.query.ProcessNaverOAuthUserService; import com.somemore.auth.redirect.RedirectUseCase; -import com.somemore.volunteer.usecase.query.FindVolunteerIdUseCase; +import com.somemore.volunteer.usecase.FindVolunteerIdUseCase; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -29,10 +28,10 @@ public class CustomOAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHan private final ProcessNaverOAuthUserService processNaverOAuthService; private final FindVolunteerIdUseCase findVolunteerIdUseCase; private final GenerateTokensOnLoginUseCase generateTokensOnLoginUseCase; - private final SetCookieUseCase setCookieUseCase; + private final CookieUseCase cookieUseCase; private final RedirectUseCase redirectUseCase; - @Value("${frontend.url}") + @Value("${app.front-url}") private String frontendRootUrl; @Override @@ -49,7 +48,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo UUID volunteerId = findVolunteerIdUseCase.findVolunteerIdByOAuthId(oAuthId); EncodedToken accessToken = generateTokensOnLoginUseCase.saveRefreshTokenAndReturnAccessToken(volunteerId); - setCookieUseCase.setToken(response, accessToken.value(), TokenType.ACCESS); + cookieUseCase.setAccessToken(response, accessToken.value()); redirectUseCase.redirect(request, response, frontendRootUrl); } From e905d8496c1cba12892f37db20ad5d03eaa928e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:24:29 +0900 Subject: [PATCH 26/40] =?UTF-8?q?feat(RefreshToken):=20=EB=A6=AC=ED=94=84?= =?UTF-8?q?=EB=A0=88=EC=8B=9C=20=ED=86=A0=ED=81=B0=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 리프레시 토큰 삭제의 파라미터를 엑세스 토큰에서 사용자 ID로 변경 - 리프레시 토큰이 존재하지 않아도 로그아웃이 가능하도록 로직 수정 - 리프레시 토큰이 없는 상태에서 로그아웃이 불가능한 것은 비정상적인 동작으로 판단 - 레포지토리에서 사용자 ID로 리프레시 토큰을 찾는 메서드 추가 --- .../jwt/refresh/manager/RedisRefreshTokenManager.java | 8 +++----- .../auth/jwt/refresh/manager/RefreshTokenManager.java | 2 +- .../jwt/refresh/repository/RefreshTokenRepository.java | 2 ++ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/somemore/auth/jwt/refresh/manager/RedisRefreshTokenManager.java b/src/main/java/com/somemore/auth/jwt/refresh/manager/RedisRefreshTokenManager.java index 0a2d57e64..9f717056c 100644 --- a/src/main/java/com/somemore/auth/jwt/refresh/manager/RedisRefreshTokenManager.java +++ b/src/main/java/com/somemore/auth/jwt/refresh/manager/RedisRefreshTokenManager.java @@ -26,10 +26,8 @@ public void save(RefreshToken refreshToken) { } @Override - public void removeRefreshToken(EncodedToken accessToken) { - RefreshToken refreshToken = refreshTokenRepository.findByAccessToken(accessToken.value()) - .orElseThrow(() -> new JwtException(JwtErrorType.EXPIRED_TOKEN)); - - refreshTokenRepository.delete(refreshToken); + public void removeRefreshToken(String userId) { + refreshTokenRepository.findByUserId(userId) + .ifPresent(refreshTokenRepository::delete); } } diff --git a/src/main/java/com/somemore/auth/jwt/refresh/manager/RefreshTokenManager.java b/src/main/java/com/somemore/auth/jwt/refresh/manager/RefreshTokenManager.java index eb27a6582..ec2c0b1a7 100644 --- a/src/main/java/com/somemore/auth/jwt/refresh/manager/RefreshTokenManager.java +++ b/src/main/java/com/somemore/auth/jwt/refresh/manager/RefreshTokenManager.java @@ -8,5 +8,5 @@ public interface RefreshTokenManager { void save(RefreshToken refreshToken); - void removeRefreshToken(EncodedToken accessToken); + void removeRefreshToken(String userId); } diff --git a/src/main/java/com/somemore/auth/jwt/refresh/repository/RefreshTokenRepository.java b/src/main/java/com/somemore/auth/jwt/refresh/repository/RefreshTokenRepository.java index 9eb75e24a..31a566751 100644 --- a/src/main/java/com/somemore/auth/jwt/refresh/repository/RefreshTokenRepository.java +++ b/src/main/java/com/somemore/auth/jwt/refresh/repository/RefreshTokenRepository.java @@ -10,4 +10,6 @@ public interface RefreshTokenRepository extends CrudRepository { Optional findByAccessToken(String accessToken); + + Optional findByUserId(String userId); } From 10cf39889e593a909f1c751b42311233672d9d18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:27:01 +0900 Subject: [PATCH 27/40] =?UTF-8?q?feat(SignOut):=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EB=B0=8F=20=EB=A6=AC=ED=94=84=EB=A0=88=EC=8B=9C=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EC=82=AD=EC=A0=9C=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로그아웃 시 액세스 토큰 쿠키를 삭제(덮어씌우기)하는 로직 추가 - 리프레시 토큰을 사용자 ID를 기반으로 삭제하도록 처리 --- .../service/SignOutVolunteerService.java | 30 +++++++++++++++++++ .../usecase/SignOutVolunteerUseCase.java | 8 +++++ 2 files changed, 38 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java create mode 100644 src/main/java/com/somemore/volunteer/usecase/SignOutVolunteerUseCase.java diff --git a/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java b/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java new file mode 100644 index 000000000..be75af84e --- /dev/null +++ b/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java @@ -0,0 +1,30 @@ +package com.somemore.volunteer.service; + +import com.somemore.auth.cookie.CookieUseCase; +import com.somemore.auth.jwt.refresh.manager.RefreshTokenManager; +import com.somemore.volunteer.usecase.SignOutVolunteerUseCase; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional +public class SignOutVolunteerService implements SignOutVolunteerUseCase { + + private final CookieUseCase cookieUseCase; + private final RefreshTokenManager refreshTokenManager; + + @Override + public void signOut( + HttpServletResponse response, + @AuthenticationPrincipal String volunteerId) { + + cookieUseCase.deleteAccessToken(response); + refreshTokenManager.removeRefreshToken(volunteerId); + } +} diff --git a/src/main/java/com/somemore/volunteer/usecase/SignOutVolunteerUseCase.java b/src/main/java/com/somemore/volunteer/usecase/SignOutVolunteerUseCase.java new file mode 100644 index 000000000..b5a236ed6 --- /dev/null +++ b/src/main/java/com/somemore/volunteer/usecase/SignOutVolunteerUseCase.java @@ -0,0 +1,8 @@ +package com.somemore.volunteer.usecase; + +import jakarta.servlet.http.HttpServletResponse; + +public interface SignOutVolunteerUseCase { + + void signOut(HttpServletResponse response, String volunteerId); +} From ccf9c707b391c74c8adc903cf0508b1ee29e9dce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:42:28 +0900 Subject: [PATCH 28/40] =?UTF-8?q?test(CookieService):=20=EC=95=A1=EC=84=B8?= =?UTF-8?q?=EC=8A=A4=20=ED=86=A0=ED=81=B0=20=EC=84=A4=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C=20=EB=A1=9C=EC=A7=81=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 액세스 토큰 설정 동작 검증 - 토큰 값, HttpOnly, Secure, Path 등 쿠키 속성 확인 - 액세스 토큰 삭제 동작 검증 - 쿠키 값에 SIGNOUT이 포함된 것을 확인 - 쿠키 값이 삭제(Max-Age=0)되었는지 확인 --- .../auth/cookie/CookieServiceTest.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/test/java/com/somemore/auth/cookie/CookieServiceTest.java diff --git a/src/test/java/com/somemore/auth/cookie/CookieServiceTest.java b/src/test/java/com/somemore/auth/cookie/CookieServiceTest.java new file mode 100644 index 000000000..6dbab7163 --- /dev/null +++ b/src/test/java/com/somemore/auth/cookie/CookieServiceTest.java @@ -0,0 +1,44 @@ +package com.somemore.auth.cookie; + +import com.somemore.auth.jwt.domain.TokenType; +import org.junit.jupiter.api.Test; +import org.springframework.mock.web.MockHttpServletResponse; + +import static org.assertj.core.api.Assertions.assertThat; + +class CookieServiceTest { + + private final CookieService cookieService = new CookieService(); + + @Test + void setAccessToken_ShouldSetCookie() { + // Given + MockHttpServletResponse response = new MockHttpServletResponse(); + String accessToken = "test-access-token"; + + // When + cookieService.setAccessToken(response, accessToken); + + // Then + String setCookieHeader = response.getHeader("Set-Cookie"); + assertThat(setCookieHeader).contains("ACCESS=" + accessToken); + assertThat(setCookieHeader).contains("HttpOnly"); + assertThat(setCookieHeader).contains("Secure"); + assertThat(setCookieHeader).contains("Path=/"); + } + + @Test + void deleteAccessToken_ShouldRemoveCookie() { + // Given + MockHttpServletResponse response = new MockHttpServletResponse(); + + // When + cookieService.deleteAccessToken(response); + + // Then + String setCookieHeader = response.getHeader("Set-Cookie"); + assertThat(setCookieHeader).contains("ACCESS=" + TokenType.SIGNOUT.name()); // 빈 값 + assertThat(setCookieHeader).contains("Max-Age=0"); // 삭제 + assertThat(setCookieHeader).contains("Path=/"); + } +} \ No newline at end of file From 833c61d724ea398b5053d22433e8f882bd21eabb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:44:38 +0900 Subject: [PATCH 29/40] =?UTF-8?q?feat(OAuth):=20OAuth=20URL=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OAuth 인증 URL을 동적으로 생성 - 백엔드 Root URL과 OAuth Provider 이름을 조합하여 URL 생성 - /oauth2/authorization/{provider} 형태의 URL 반환 로직 구현 - 환경에 따라 백엔드 Root URL을 설정할 수 있도록 @Value("${app.back-url}") 적용 --- .../service/GenerateOAuthUrlService.java | 32 +++++++++++++++++++ .../usecase/GenerateOAuthUrlUseCase.java | 5 +++ 2 files changed, 37 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/service/GenerateOAuthUrlService.java create mode 100644 src/main/java/com/somemore/volunteer/usecase/GenerateOAuthUrlUseCase.java diff --git a/src/main/java/com/somemore/volunteer/service/GenerateOAuthUrlService.java b/src/main/java/com/somemore/volunteer/service/GenerateOAuthUrlService.java new file mode 100644 index 000000000..8e179e5d4 --- /dev/null +++ b/src/main/java/com/somemore/volunteer/service/GenerateOAuthUrlService.java @@ -0,0 +1,32 @@ +package com.somemore.volunteer.service; + +import com.somemore.volunteer.usecase.GenerateOAuthUrlUseCase; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.util.UriComponentsBuilder; + +@Slf4j +@Service +@RequiredArgsConstructor +public class GenerateOAuthUrlService implements GenerateOAuthUrlUseCase { + + @Value("${app.back-url}") + private String backendRootUrl; + + @Override + public String generateUrl(String oAuthProvider) { + return UriComponentsBuilder.fromHttpUrl(generateBaseUrl()) + .pathSegment(oAuthProvider) + .build() + .toUriString(); + } + + private String generateBaseUrl() { + return UriComponentsBuilder.fromHttpUrl(backendRootUrl) + .path("/oauth2/authorization") + .build() + .toUriString(); + } +} diff --git a/src/main/java/com/somemore/volunteer/usecase/GenerateOAuthUrlUseCase.java b/src/main/java/com/somemore/volunteer/usecase/GenerateOAuthUrlUseCase.java new file mode 100644 index 000000000..1a91f69c8 --- /dev/null +++ b/src/main/java/com/somemore/volunteer/usecase/GenerateOAuthUrlUseCase.java @@ -0,0 +1,5 @@ +package com.somemore.volunteer.usecase; + +public interface GenerateOAuthUrlUseCase { + String generateUrl(String oAuthProvider); +} From cb454b0b9ecc2a615e77c1618e703205a628c75f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 12:46:25 +0900 Subject: [PATCH 30/40] =?UTF-8?q?test(GenerateOAuthUrlService):=20OAuth=20?= =?UTF-8?q?URL=20=EC=83=9D=EC=84=B1=20=EB=A1=9C=EC=A7=81=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OAuth Provider에 따라 올바른 URL이 생성되는지 검증 --- .../service/GenerateOAuthUrlServiceTest.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/test/java/com/somemore/volunteer/service/GenerateOAuthUrlServiceTest.java diff --git a/src/test/java/com/somemore/volunteer/service/GenerateOAuthUrlServiceTest.java b/src/test/java/com/somemore/volunteer/service/GenerateOAuthUrlServiceTest.java new file mode 100644 index 000000000..be5d20140 --- /dev/null +++ b/src/test/java/com/somemore/volunteer/service/GenerateOAuthUrlServiceTest.java @@ -0,0 +1,46 @@ +package com.somemore.volunteer.service; + +import com.somemore.IntegrationTestSupport; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; + +import static org.assertj.core.api.Assertions.assertThat; + +class GenerateOAuthUrlServiceTest extends IntegrationTestSupport { + + @Autowired + private GenerateOAuthUrlService generateOAuthUrlService; + + @Value("${app.back-url}") + private String backendRootUrl; + + + @Test + void generateUrl_ShouldReturnCorrectUrl_ForNaver() { + // Given + String oAuthProvider = "naver"; + + // When + String result = generateOAuthUrlService.generateUrl(oAuthProvider); + + // Then + String expectedUrl = backendRootUrl + "/oauth2/authorization/naver"; + + assertThat(result).isEqualTo(expectedUrl); + } + + @Test + void generateUrl_ShouldReturnCorrectUrl_ForGoogle() { + // Given + String oAuthProvider = "google"; + + // When + String result = generateOAuthUrlService.generateUrl(oAuthProvider); + + // Then + String expectedUrl = backendRootUrl + "/oauth2/authorization/google"; + + assertThat(result).isEqualTo(expectedUrl); + } +} \ No newline at end of file From bf887358c02fa49f19e90b847bf4a6c9001578a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:14:21 +0900 Subject: [PATCH 31/40] =?UTF-8?q?test(SignOutVolunteerService):=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83=20=EB=8F=99=EC=9E=91=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 테스트 후 레디스 delete all - 액세스 토큰 쿠키 삭제 및 리프레시 토큰 제거 검증 - 리프레시 토큰이 없는 경우에도 예외가 발생하지 않는지 확인 - 테스트 시 MockHttpServletResponse를 활용하여 쿠키 동작 검증 - 쿠키 name이 중복으로 저장되는 문제 회피 (트러블슈팅 기록) --- .../auth/jwt/service/JwtServiceTest.java | 12 +++ .../service/SignOutVolunteerServiceTest.java | 93 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java diff --git a/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java b/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java index 509c513ce..600b16250 100644 --- a/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java +++ b/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java @@ -11,10 +11,13 @@ import com.somemore.auth.jwt.validator.JwtValidator; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.transaction.annotation.Transactional; import javax.crypto.SecretKey; import java.time.Instant; @@ -34,6 +37,15 @@ class JwtServiceTest extends IntegrationTestSupport { private SecretKey secretKey; @Autowired private RefreshTokenManager refreshTokenManager; + @Autowired + private RedisTemplate redisTemplate; + + + @AfterEach + void tearDown() { + redisTemplate.keys("*") + .forEach(redisTemplate::delete); + } @DisplayName("토큰이 올바르게 생성된다") @Test diff --git a/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java b/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java new file mode 100644 index 000000000..83bdc06a2 --- /dev/null +++ b/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java @@ -0,0 +1,93 @@ +package com.somemore.volunteer.service; + +import com.somemore.IntegrationTestSupport; +import com.somemore.auth.cookie.CookieUseCase; +import com.somemore.auth.jwt.domain.EncodedToken; +import com.somemore.auth.jwt.domain.TokenType; +import com.somemore.auth.jwt.exception.JwtErrorType; +import com.somemore.auth.jwt.exception.JwtException; +import com.somemore.auth.jwt.filter.UserRole; +import com.somemore.auth.jwt.generator.JwtGenerator; +import com.somemore.auth.jwt.refresh.domain.RefreshToken; +import com.somemore.auth.jwt.refresh.manager.RefreshTokenManager; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; + +import static org.assertj.core.api.Assertions.*; + +@Transactional +class SignOutVolunteerServiceTest extends IntegrationTestSupport { + + @Autowired + private SignOutVolunteerService signOutVolunteerService; + @Autowired + private CookieUseCase cookieUseCase; + @Autowired + private RefreshTokenManager refreshTokenManager; + @Autowired + private JwtGenerator jwtGenerator; + @Autowired + private RedisTemplate redisTemplate; + + private MockHttpServletResponse response; + private String volunteerId; + private UserRole role; + + + @BeforeEach + void setUp() { + response = new MockHttpServletResponse(); + volunteerId = "test-volunteer"; + role = UserRole.VOLUNTEER; + } + + @AfterEach + void tearDown() { + redisTemplate.keys("*") + .forEach(redisTemplate::delete); + } + + @Test + @DisplayName("로그아웃 시 액세스 토큰 쿠키를 삭제하고 리프레시 토큰을 제거해야 한다.") + void signOutDeletesTokens() { + // Given + EncodedToken accessToken = jwtGenerator.generateToken(volunteerId, role.name(), TokenType.ACCESS); + + RefreshToken refreshToken = new RefreshToken( + volunteerId, + accessToken, + jwtGenerator.generateToken(volunteerId, role.name(), TokenType.REFRESH)); + + refreshTokenManager.save(refreshToken); + cookieUseCase.setAccessToken(response, accessToken.value()); + + // When + signOutVolunteerService.signOut(response, volunteerId); + + // Then + assertThatThrownBy(() -> refreshTokenManager.findRefreshToken(accessToken)) + .isInstanceOf(JwtException.class) + .hasMessage(JwtErrorType.EXPIRED_TOKEN.getMessage()); + + assertThat(Arrays.toString(response.getCookies())).contains(TokenType.SIGNOUT.name()); + } + + @Test + @DisplayName("로그아웃 시 리프레시 토큰이 없어도 예외가 발생하지 않는다.") + void signOutWithoutRefreshToken() { + // When + signOutVolunteerService.signOut(response, volunteerId); + + // Then + assertThatNoException().isThrownBy(() -> signOutVolunteerService.signOut(response, volunteerId)); + assertThat(Arrays.toString(response.getCookies())).contains(TokenType.SIGNOUT.name()); + } +} \ No newline at end of file From e2d337dd46f5284c56c03e202994e87b6639bb7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:18:35 +0900 Subject: [PATCH 32/40] =?UTF-8?q?feat(application.yml):=20front/back=20url?= =?UTF-8?q?=20=EB=B3=80=EC=88=98=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b4eb6c368..0484bb2aa 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,6 +1,6 @@ app: - front-url: "http://localhost:3000" - back-url: "http://localhost:8080" + front-url: ${FRONT_URL} + back-url: ${BACK_URL} spring: datasource: From ce63e5925ff091ec62a1234bb46f1d12e27d9fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:20:17 +0900 Subject: [PATCH 33/40] =?UTF-8?q?feat(VolunteerSignController):=20OAuth=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=95=84=EC=9B=83=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - OAuth 로그인 URL 생성 API 추가 - [POST] /api/volunteer/sign-in/oauth/{oauthProvider} - 지원되는 OAuth 제공자에 따라 리다이렉트 URL 생성 및 반환 - 지원되지 않는 OAuth 제공자는 `BadRequestException` 처리 - 로그아웃 API 추가 - [POST] /api/volunteer/sign-out - 액세스 토큰 쿠키 삭제 및 리프레시 토큰 제거 - 성공 시 표준 응답 형식(`ApiResponse.ok`)으로 메시지 반환 - GenerateOAuthUrlUseCase를 사용해 OAuth URL 동적 생성 - SignOutVolunteerUseCase를 사용해 로그아웃 로직 처리 --- .../query/VolunteerSignController.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/main/java/com/somemore/volunteer/controller/query/VolunteerSignController.java diff --git a/src/main/java/com/somemore/volunteer/controller/query/VolunteerSignController.java b/src/main/java/com/somemore/volunteer/controller/query/VolunteerSignController.java new file mode 100644 index 000000000..4e7660483 --- /dev/null +++ b/src/main/java/com/somemore/volunteer/controller/query/VolunteerSignController.java @@ -0,0 +1,53 @@ +package com.somemore.volunteer.controller.query; + +import com.somemore.global.common.response.ApiResponse; +import com.somemore.global.exception.BadRequestException; +import com.somemore.volunteer.usecase.SignOutVolunteerUseCase; +import com.somemore.volunteer.usecase.GenerateOAuthUrlUseCase; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.view.RedirectView; + +@RestController +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/api/volunteer") +@Tag(name = "Volunteer OAuth API", description = "Handles Volunteer OAuth Sign-in, Sign-out") +public class VolunteerSignController { + + private final GenerateOAuthUrlUseCase generateOAuthUrlUseCase; + private final SignOutVolunteerUseCase signOutVolunteerUseCase; + + @PostMapping("/sign-in/oauth/{oauthProvider}") + public RedirectView signIn( + @Parameter(name = "oauthProvider", description = "OAuth 제공자 선택", example = "naver", required = true, schema = @Schema(allowableValues = {"naver"})) + @PathVariable("oauthProvider") String oauthProvider) { + + String redirectUrl = switch (oauthProvider.toLowerCase()) { + case "naver" -> generateOAuthUrlUseCase.generateUrl(oauthProvider); + + default -> throw new BadRequestException("지원되지 않는 OAuth 제공자: " + oauthProvider); + }; + + return new RedirectView(redirectUrl); + } + + @PostMapping("/sign-out") + public ApiResponse signOut( + HttpServletResponse response, + @AuthenticationPrincipal String userId) { + + signOutVolunteerUseCase.signOut(response, userId); + + return ApiResponse.ok("로그아웃되었습니다"); + } +} From 9a86240cbd43da2808b3e97055c0412eb3069a10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:23:03 +0900 Subject: [PATCH 34/40] =?UTF-8?q?feat(application.yml):=20naver-redirect-u?= =?UTF-8?q?ri=20=EB=B3=80=EC=88=98=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0484bb2aa..be25b9b53 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -32,7 +32,7 @@ spring: naver: client-id: ${NAVER_CLIENT_ID} client-secret: ${NAVER_CLIENT_SECRET} - redirect-uri: "http://localhost:8080/login/oauth2/code/naver" + redirect-uri: ${NAVER_REDIRECT_URL} authorization-grant-type: authorization_code scope: ${NAVER_SCOPE} From 8cd40c90c174b60dbbf3b4b362b6a089c0dd6d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:23:50 +0900 Subject: [PATCH 35/40] =?UTF-8?q?refactor(package):=20C/Q=20=EA=B5=AC?= =?UTF-8?q?=EB=B6=84=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/{command => }/GenerateTokensOnLoginService.java | 4 ++-- .../usecase/{command => }/GenerateTokensOnLoginUseCase.java | 2 +- .../auth/oauth/handler/success/CustomOAuthSuccessHandler.java | 2 +- .../controller/{query => }/VolunteerSignController.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename src/main/java/com/somemore/auth/jwt/service/{command => }/GenerateTokensOnLoginService.java (93%) rename src/main/java/com/somemore/auth/jwt/usecase/{command => }/GenerateTokensOnLoginUseCase.java (80%) rename src/main/java/com/somemore/volunteer/controller/{query => }/VolunteerSignController.java (97%) diff --git a/src/main/java/com/somemore/auth/jwt/service/command/GenerateTokensOnLoginService.java b/src/main/java/com/somemore/auth/jwt/service/GenerateTokensOnLoginService.java similarity index 93% rename from src/main/java/com/somemore/auth/jwt/service/command/GenerateTokensOnLoginService.java rename to src/main/java/com/somemore/auth/jwt/service/GenerateTokensOnLoginService.java index 4bbbbd74d..a0cd82ee3 100644 --- a/src/main/java/com/somemore/auth/jwt/service/command/GenerateTokensOnLoginService.java +++ b/src/main/java/com/somemore/auth/jwt/service/GenerateTokensOnLoginService.java @@ -1,4 +1,4 @@ -package com.somemore.auth.jwt.service.command; +package com.somemore.auth.jwt.service; import com.somemore.auth.jwt.domain.UserRole; import com.somemore.auth.jwt.domain.EncodedToken; @@ -6,7 +6,7 @@ import com.somemore.auth.jwt.generator.JwtGenerator; import com.somemore.auth.jwt.refresh.domain.RefreshToken; import com.somemore.auth.jwt.refresh.manager.RefreshTokenManager; -import com.somemore.auth.jwt.usecase.command.GenerateTokensOnLoginUseCase; +import com.somemore.auth.jwt.usecase.GenerateTokensOnLoginUseCase; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; diff --git a/src/main/java/com/somemore/auth/jwt/usecase/command/GenerateTokensOnLoginUseCase.java b/src/main/java/com/somemore/auth/jwt/usecase/GenerateTokensOnLoginUseCase.java similarity index 80% rename from src/main/java/com/somemore/auth/jwt/usecase/command/GenerateTokensOnLoginUseCase.java rename to src/main/java/com/somemore/auth/jwt/usecase/GenerateTokensOnLoginUseCase.java index 912f205f0..0c48a4cce 100644 --- a/src/main/java/com/somemore/auth/jwt/usecase/command/GenerateTokensOnLoginUseCase.java +++ b/src/main/java/com/somemore/auth/jwt/usecase/GenerateTokensOnLoginUseCase.java @@ -1,4 +1,4 @@ -package com.somemore.auth.jwt.usecase.command; +package com.somemore.auth.jwt.usecase; import com.somemore.auth.jwt.domain.EncodedToken; diff --git a/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java b/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java index d26edadfd..b090b8db4 100644 --- a/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java +++ b/src/main/java/com/somemore/auth/oauth/handler/success/CustomOAuthSuccessHandler.java @@ -2,7 +2,7 @@ import com.somemore.auth.cookie.CookieUseCase; import com.somemore.auth.jwt.domain.EncodedToken; -import com.somemore.auth.jwt.usecase.command.GenerateTokensOnLoginUseCase; +import com.somemore.auth.jwt.usecase.GenerateTokensOnLoginUseCase; import com.somemore.auth.oauth.OAuthProvider; import com.somemore.auth.oauth.naver.service.query.ProcessNaverOAuthUserService; import com.somemore.auth.redirect.RedirectUseCase; diff --git a/src/main/java/com/somemore/volunteer/controller/query/VolunteerSignController.java b/src/main/java/com/somemore/volunteer/controller/VolunteerSignController.java similarity index 97% rename from src/main/java/com/somemore/volunteer/controller/query/VolunteerSignController.java rename to src/main/java/com/somemore/volunteer/controller/VolunteerSignController.java index 4e7660483..9d7f3445a 100644 --- a/src/main/java/com/somemore/volunteer/controller/query/VolunteerSignController.java +++ b/src/main/java/com/somemore/volunteer/controller/VolunteerSignController.java @@ -1,4 +1,4 @@ -package com.somemore.volunteer.controller.query; +package com.somemore.volunteer.controller; import com.somemore.global.common.response.ApiResponse; import com.somemore.global.exception.BadRequestException; From 62f20b87e9c06b13bb9266a335d49eb353ba9bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:44:30 +0900 Subject: [PATCH 36/40] =?UTF-8?q?feat(application.yml):=20UTF-8,=20KR=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.yml | 11 +++++++++++ src/test/resources/application-test.yml | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index be25b9b53..73394baa0 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -43,6 +43,10 @@ spring: user-info-uri: https://openapi.naver.com/v1/nid/me user-name-attribute: response # 네이버 API가 사용자 정보를 "response" 객체 안에 넣어 반환 + web: + locale: ko_KR + locale-resolver: fixed + #swagger springdoc: swagger-ui: @@ -68,3 +72,10 @@ jwt: logging: level: org.springframework.security: DEBUG + +server: + servlet: + encoding: + charset: UTF-8 + enabled: true + force: true diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index a53533e3e..fab7dd965 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -31,5 +31,16 @@ spring: port: 6379 password: # 테스트에서는 비밀번호 없이 연결 + web: + locale: ko_KR + locale-resolver: fixed + jwt: secret: 63bf2c80266cd25072e53b3482e318c30d1cd18d8c98d0f5d278530a94fe28d9fbbec531e5ccb58c725c125738182357786b71f43a7172c5d0c94a17f0da44f2 # 테스트용 JWT 시크릿 키 + +server: + servlet: + encoding: + charset: UTF-8 + enabled: true + force: true From 15d8dd0d89882c51f3be9c4b89004350b750fdaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:45:00 +0900 Subject: [PATCH 37/40] =?UTF-8?q?test(VolunteerSignController):=20OAuth=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 유효한 OAuth 제공자로 로그인 URL 생성 테스트 추가 - 지원되지 않는 OAuth 제공자에 대해 400 에러 반환 테스트 추가 - 로그아웃 요청 시 성공 메시지 반환 테스트 추가 --- .../VolunteerSignControllerTest.java | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/test/java/com/somemore/volunteer/controller/VolunteerSignControllerTest.java diff --git a/src/test/java/com/somemore/volunteer/controller/VolunteerSignControllerTest.java b/src/test/java/com/somemore/volunteer/controller/VolunteerSignControllerTest.java new file mode 100644 index 000000000..1af24e071 --- /dev/null +++ b/src/test/java/com/somemore/volunteer/controller/VolunteerSignControllerTest.java @@ -0,0 +1,66 @@ +package com.somemore.volunteer.controller; + +import com.somemore.IntegrationTestSupport; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.web.servlet.MockMvc; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +class VolunteerSignControllerTest extends IntegrationTestSupport { + + @Autowired + private MockMvc mockMvc; + + @Test + @DisplayName("유효한 OAuth 제공자로 로그인 URL을 생성한다.") + void signInWithValidProvider() throws Exception { + // Given + String oauthProvider = "naver"; + + // When + // Then + mockMvc.perform(post("/api/volunteer/sign-in/oauth/{oauthProvider}", oauthProvider)) + .andExpect(status().is3xxRedirection()) + .andExpect(result -> { + MockHttpServletResponse response = result.getResponse(); + String redirectedUrl = response.getRedirectedUrl(); + assertThat(redirectedUrl).isNotNull(); + assertThat(redirectedUrl).contains("oauth2/authorization/naver"); + }); + } + + @Test + @DisplayName("지원되지 않는 OAuth 제공자로 로그인 시 400 에러를 반환한다.") + void signInWithInvalidProvider() throws Exception { + // Given + String invalidProvider = "unsupported-provider"; + + // When + // Then + mockMvc.perform(post("/api/volunteer/sign-in/oauth/{oauthProvider}", invalidProvider)) + .andExpect(status().isBadRequest()); + } + + @Test + @DisplayName("로그아웃 요청 시 성공 메시지를 반환한다.") + void signOut() throws Exception { + // When + // Then + mockMvc.perform(post("/api/volunteer/sign-out")) + .andExpect(status().isOk()) + .andExpect(result -> { + MockHttpServletResponse response = result.getResponse(); + String responseBody = response.getContentAsString(); + assertThat(responseBody).contains("로그아웃되었습니다"); + }); + } +} \ No newline at end of file From c6fb4340be1792bf667fa897e6806f1262161aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:46:19 +0900 Subject: [PATCH 38/40] =?UTF-8?q?test(@Transactional):=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=ED=8A=B8=EB=9E=9C=EC=9E=AD?= =?UTF-8?q?=EC=85=98=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java | 1 - .../somemore/volunteer/service/SignOutVolunteerServiceTest.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java b/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java index 600b16250..0519bcd3a 100644 --- a/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java +++ b/src/test/java/com/somemore/auth/jwt/service/JwtServiceTest.java @@ -17,7 +17,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.transaction.annotation.Transactional; import javax.crypto.SecretKey; import java.time.Instant; diff --git a/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java b/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java index 83bdc06a2..9a442c7dc 100644 --- a/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java +++ b/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java @@ -23,7 +23,6 @@ import static org.assertj.core.api.Assertions.*; -@Transactional class SignOutVolunteerServiceTest extends IntegrationTestSupport { @Autowired From 0c8bf5c07ba92a9103ae4363b330eca3572f2471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:46:46 +0900 Subject: [PATCH 39/40] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=96=B4=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/somemore/volunteer/service/SignOutVolunteerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java b/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java index be75af84e..b2605039f 100644 --- a/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java +++ b/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java @@ -22,7 +22,7 @@ public class SignOutVolunteerService implements SignOutVolunteerUseCase { @Override public void signOut( HttpServletResponse response, - @AuthenticationPrincipal String volunteerId) { + String volunteerId) { cookieUseCase.deleteAccessToken(response); refreshTokenManager.removeRefreshToken(volunteerId); From fe96db10d134e2f44bc629ca794e98af6099c33c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Wed, 27 Nov 2024 22:11:05 +0900 Subject: [PATCH 40/40] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/somemore/volunteer/service/SignOutVolunteerService.java | 1 - .../somemore/volunteer/service/SignOutVolunteerServiceTest.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java b/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java index b2605039f..95e015d0f 100644 --- a/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java +++ b/src/main/java/com/somemore/volunteer/service/SignOutVolunteerService.java @@ -6,7 +6,6 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; diff --git a/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java b/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java index 9a442c7dc..c6c9efd15 100644 --- a/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java +++ b/src/test/java/com/somemore/volunteer/service/SignOutVolunteerServiceTest.java @@ -17,7 +17,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.transaction.annotation.Transactional; import java.util.Arrays;