From 51b6db59c9277b07ddad4e88f2fd54efa96253ae Mon Sep 17 00:00:00 2001 From: zangsu Date: Sun, 6 Oct 2024 18:38:45 +0900 Subject: [PATCH 01/66] =?UTF-8?q?refactor:=20required=20=EC=86=8D=EC=84=B1?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/codezap/auth/configuration/AuthenticationPrinciple.java | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/main/java/codezap/auth/configuration/AuthenticationPrinciple.java b/backend/src/main/java/codezap/auth/configuration/AuthenticationPrinciple.java index a87455c4c..538e22c16 100644 --- a/backend/src/main/java/codezap/auth/configuration/AuthenticationPrinciple.java +++ b/backend/src/main/java/codezap/auth/configuration/AuthenticationPrinciple.java @@ -8,4 +8,5 @@ @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface AuthenticationPrinciple { + boolean required() default true; } From 8312373a03f0a4ee3d307a5648031864bc8a2e41 Mon Sep 17 00:00:00 2001 From: zangsu Date: Sun, 6 Oct 2024 18:51:01 +0900 Subject: [PATCH 02/66] =?UTF-8?q?refactor:=20ArgumentResolver=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20null=20=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/configuration/AuthArgumentResolver.java | 4 ++++ .../codezap/auth/manager/CookieCredentialManager.java | 10 ++++++++++ .../java/codezap/auth/manager/CredentialManager.java | 2 ++ 3 files changed, 16 insertions(+) diff --git a/backend/src/main/java/codezap/auth/configuration/AuthArgumentResolver.java b/backend/src/main/java/codezap/auth/configuration/AuthArgumentResolver.java index 4a1febf92..335167bc3 100644 --- a/backend/src/main/java/codezap/auth/configuration/AuthArgumentResolver.java +++ b/backend/src/main/java/codezap/auth/configuration/AuthArgumentResolver.java @@ -32,7 +32,11 @@ public Member resolveArgument( NativeWebRequest webRequest, WebDataBinderFactory binderFactory ) { + AuthenticationPrinciple parameterAnnotation = parameter.getParameterAnnotation(AuthenticationPrinciple.class); HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); + if(!parameterAnnotation.required() && !credentialManager.hasCredential(request)) { + return null; + } String credential = credentialManager.getCredential(request); return credentialProvider.extractMember(credential); } diff --git a/backend/src/main/java/codezap/auth/manager/CookieCredentialManager.java b/backend/src/main/java/codezap/auth/manager/CookieCredentialManager.java index 571f22640..84479058e 100644 --- a/backend/src/main/java/codezap/auth/manager/CookieCredentialManager.java +++ b/backend/src/main/java/codezap/auth/manager/CookieCredentialManager.java @@ -33,6 +33,16 @@ private void checkCookieExist(final Cookie[] cookies) { } } + @Override + public boolean hasCredential(final HttpServletRequest httpServletRequest) { + Cookie[] cookies = httpServletRequest.getCookies(); + if (cookies == null) { + return false; + } + return Arrays.stream(cookies) + .anyMatch(cookie -> cookie.getName().equals(CREDENTIAL_COOKIE_NAME)); + } + private Cookie extractTokenCookie(final Cookie[] cookies) { return Arrays.stream(cookies) .filter(cookie -> cookie.getName().equals(CREDENTIAL_COOKIE_NAME)) diff --git a/backend/src/main/java/codezap/auth/manager/CredentialManager.java b/backend/src/main/java/codezap/auth/manager/CredentialManager.java index cb3e92053..69f4df39b 100644 --- a/backend/src/main/java/codezap/auth/manager/CredentialManager.java +++ b/backend/src/main/java/codezap/auth/manager/CredentialManager.java @@ -7,6 +7,8 @@ public interface CredentialManager { String getCredential(HttpServletRequest httpServletRequest); + boolean hasCredential(final HttpServletRequest httpServletRequest); + void setCredential(HttpServletResponse httpServletResponse, String credential); void removeCredential(HttpServletResponse httpServletResponse); From 82d27a32487ff41c00565a5ebd1e2e08c0f36a0e Mon Sep 17 00:00:00 2001 From: zangsu Date: Sun, 6 Oct 2024 21:35:06 +0900 Subject: [PATCH 03/66] =?UTF-8?q?test:=20AuthArgumentResolver=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=EC=9D=84=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/manager/CookieCredentialManager.java | 2 +- .../AuthArgumentResolverTest.java | 168 ++++++++++++++++++ .../provider/PlainCredentialProvider.java | 27 +++ 3 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 backend/src/test/java/codezap/auth/configuration/AuthArgumentResolverTest.java create mode 100644 backend/src/test/java/codezap/auth/provider/PlainCredentialProvider.java diff --git a/backend/src/main/java/codezap/auth/manager/CookieCredentialManager.java b/backend/src/main/java/codezap/auth/manager/CookieCredentialManager.java index 84479058e..4e571838a 100644 --- a/backend/src/main/java/codezap/auth/manager/CookieCredentialManager.java +++ b/backend/src/main/java/codezap/auth/manager/CookieCredentialManager.java @@ -16,7 +16,7 @@ @Component public class CookieCredentialManager implements CredentialManager { - private static final String CREDENTIAL_COOKIE_NAME = "credential"; + public static final String CREDENTIAL_COOKIE_NAME = "credential"; @Override public String getCredential(final HttpServletRequest httpServletRequest) { diff --git a/backend/src/test/java/codezap/auth/configuration/AuthArgumentResolverTest.java b/backend/src/test/java/codezap/auth/configuration/AuthArgumentResolverTest.java new file mode 100644 index 000000000..9926a87d2 --- /dev/null +++ b/backend/src/test/java/codezap/auth/configuration/AuthArgumentResolverTest.java @@ -0,0 +1,168 @@ +package codezap.auth.configuration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import static codezap.auth.manager.CookieCredentialManager.CREDENTIAL_COOKIE_NAME; + +import java.lang.reflect.Method; + +import jakarta.servlet.http.Cookie; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.support.ReflectionSupport; +import org.springframework.core.MethodParameter; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.async.StandardServletAsyncWebRequest; +import org.springframework.web.method.support.ModelAndViewContainer; + +import codezap.auth.manager.CookieCredentialManager; +import codezap.auth.manager.CredentialManager; +import codezap.auth.provider.CredentialProvider; +import codezap.auth.provider.PlainCredentialProvider; +import codezap.fixture.MemberFixture; +import codezap.global.exception.CodeZapException; +import codezap.member.domain.Member; + +class AuthArgumentResolverTest { + CredentialManager credentialManager = new CookieCredentialManager(); + CredentialProvider credentialProvider = new PlainCredentialProvider(); + AuthArgumentResolver authArgumentResolver = new AuthArgumentResolver(credentialManager, credentialProvider); + + @Nested + @DisplayName("지원하는 파라미터 테스트") + class SupportsParameterTest { + + static class SupportTestController { + public void supportMethod(@AuthenticationPrinciple Member member) { + } + + public void notSupportMethod(Member member) { + } + } + + @Test + @DisplayName("AuthenticationPrinciple 어노테이션이 파라미터에 존재하면 지원할 수 있는 메서드이다.") + void canSupportsTest() { + Method supportMethod = ReflectionSupport.findMethod( + SupportTestController.class, "supportMethod", Member.class) + .get(); + MethodParameter methodParameter = new MethodParameter(supportMethod, 0); + + assertThat(authArgumentResolver.supportsParameter(methodParameter)) + .isTrue(); + } + + @Test + @DisplayName("AuthenticationPrinciple 어노테이션이 파라미터에 존재하지 않으면 지원할 수 없는 메서드이다.") + void notSupportsTest() { + Method supportMethod = ReflectionSupport.findMethod( + SupportTestController.class, "notSupportMethod", Member.class) + .get(); + MethodParameter methodParameter = new MethodParameter(supportMethod, 0); + + assertThat(authArgumentResolver.supportsParameter(methodParameter)) + .isFalse(); + } + } + + @Nested + @DisplayName("파라미터 반환 테스트") + class ResolveArgument { + + static class ResolveTestController { + public void notRequiredMethod(@AuthenticationPrinciple(required = false) Member member) { + } + + public void requiredMethod(@AuthenticationPrinciple Member member) { + } + } + + MockHttpServletRequest httpServletRequest = new MockHttpServletRequest("GET", "/templates"); + StandardServletAsyncWebRequest nativeWebRequest = + new StandardServletAsyncWebRequest(httpServletRequest, new MockHttpServletResponse()); + Member member = MemberFixture.getFirstMember(); + Cookie credentialCookie = new Cookie(CREDENTIAL_COOKIE_NAME, credentialProvider.createCredential(member)); + + @Nested + @DisplayName("required 값이 false 라면") + class RequiredFalseTest { + Method notRequiredMethod = ReflectionSupport.findMethod(ResolveTestController.class, + "notRequiredMethod", Member.class) + .get(); + + @Test + @DisplayName("credential 정보가 없을때 null 이 반환된다.") + void noCredentialTest() { + //given + + //when + Member member = resolveArgument(notRequiredMethod, nativeWebRequest); + + //then + assertThat(member).isNull(); + } + + @Test + @DisplayName("credential 정보가 존재하면 Member 가 반환된다.") + void existCredentialTest() { + //given + httpServletRequest.setCookies(credentialCookie); + + //when + Member resolvedArgument = resolveArgument(notRequiredMethod, nativeWebRequest); + + //then + assertThat(resolvedArgument).isEqualTo(member); + } + } + + @Nested + @DisplayName("required 값이 true 라면") + class RequiredTrueTest { + Method requiredMethod = ReflectionSupport.findMethod(ResolveTestController.class, + "requiredMethod", Member.class) + .get(); + + @Test + @DisplayName("credential 정보가 없을때 예외가 발생한다..") + void noCredentialTest() { + //given + + //when & then + assertThatThrownBy(() -> resolveArgument(requiredMethod, nativeWebRequest)) + .isInstanceOf(CodeZapException.class) + .hasMessage("쿠키가 없어서 회원 정보를 찾을 수 없습니다. 다시 로그인해주세요."); + } + + @Test + @DisplayName("credential 정보가 존재하면 Member 가 반환된다.") + void existCredentialTest() { + //given + httpServletRequest.setCookies(credentialCookie); + + //when + Member resolvedArgument = resolveArgument(requiredMethod, nativeWebRequest); + + //then + assertThat(resolvedArgument).isEqualTo(member); + } + } + + private Member resolveArgument(Method method, NativeWebRequest webRequest) { + ModelAndViewContainer modelAndViewContainer = new ModelAndViewContainer(); + WebDataBinderFactory webDataBinderFactory = (request, target, objectName) -> null; + + return authArgumentResolver.resolveArgument( + new MethodParameter(method, 0), + modelAndViewContainer, + webRequest, + webDataBinderFactory); + } + } +} diff --git a/backend/src/test/java/codezap/auth/provider/PlainCredentialProvider.java b/backend/src/test/java/codezap/auth/provider/PlainCredentialProvider.java new file mode 100644 index 000000000..c7f776215 --- /dev/null +++ b/backend/src/test/java/codezap/auth/provider/PlainCredentialProvider.java @@ -0,0 +1,27 @@ +package codezap.auth.provider; + +import codezap.member.domain.Member; + +public class PlainCredentialProvider implements CredentialProvider { + + private static final String DELIMITER = ";"; + @Override + public String createCredential(Member member) { + return String.join(DELIMITER, + Long.toString(member.getId()), + member.getName(), + member.getPassword(), + member.getSalt()); + } + + @Override + public Member extractMember(String credential) { + String[] memberInfo = credential.split(DELIMITER); + return new Member( + Long.parseLong(memberInfo[0]), + memberInfo[1], + memberInfo[2], + memberInfo[3] + ); + } +} From da864d94c83d5f57b0b55223ff5a8dbdea5f7a40 Mon Sep 17 00:00:00 2001 From: zangsu Date: Mon, 7 Oct 2024 01:10:35 +0900 Subject: [PATCH 04/66] =?UTF-8?q?refactor:=20=ED=8C=8C=EB=9D=BC=EB=AF=B8?= =?UTF-8?q?=ED=84=B0=20=EC=9D=B4=EB=A6=84=EC=9D=84=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=ED=95=9C=20=EB=A9=A4=EB=B2=84=EB=A5=BC=20=EB=82=98?= =?UTF-8?q?=ED=83=80=EB=82=B4=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/facade/TemplateApplicationService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/main/java/codezap/template/service/facade/TemplateApplicationService.java b/backend/src/main/java/codezap/template/service/facade/TemplateApplicationService.java index 5e64daa52..e90df403d 100644 --- a/backend/src/main/java/codezap/template/service/facade/TemplateApplicationService.java +++ b/backend/src/main/java/codezap/template/service/facade/TemplateApplicationService.java @@ -60,11 +60,11 @@ public FindTemplateResponse findById(Long id) { return FindTemplateResponse.of(template, sourceCodes, tags, false); } - public FindTemplateResponse findByIdWithMember(Long id, Member member) { + public FindTemplateResponse findByIdWithMember(Long id, Member loginMember) { Template template = templateService.getById(id); List tags = tagService.findAllByTemplate(template); List sourceCodes = sourceCodeService.findAllByTemplate(template); - boolean isLiked = likesService.isLiked(member, template); + boolean isLiked = likesService.isLiked(loginMember, template); return FindTemplateResponse.of(template, sourceCodes, tags, isLiked); } @@ -85,10 +85,10 @@ public FindAllTemplatesResponse findAllByWithMember( Long categoryId, List tagIds, Pageable pageable, - Member member + Member loginMember ) { Page