From ff73dc97b605f8ea22dddb0a8fca12c27d8e286b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Tue, 16 Jan 2024 18:58:39 +0900 Subject: [PATCH 01/89] =?UTF-8?q?docs(gitignore)=20:=20secret=20yml=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EC=9D=B4=20=EC=98=AC=EB=9D=BC=EA=B0=80?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=9D=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c2065bc2..46149dbc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ +src/main/resources/application-secret.yml ### STS ### .apt_generated From f0cdbf4decddbd3f492ce46fca2d2c8103e3e661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Tue, 16 Jan 2024 18:59:04 +0900 Subject: [PATCH 02/89] =?UTF-8?q?docs(application.yml)=20:=20secret=20yml?= =?UTF-8?q?=20=ED=8C=8C=EC=9D=BC=EC=9D=84=20=EC=B0=B8=EA=B3=A0=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=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.properties | 1 - src/main/resources/application.yml | 13 +++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) delete mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/application.yml diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 8b137891..00000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 00000000..f266cd1d --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,13 @@ +spring: + jpa: + show_sql: true + hibernate: + ddl-auto: create + + properties: + hibernate: + format_sql: true + defer-datasource-initialization: true + + profiles: + include: secret From e43e5132e74ee81a76fbcaf2daca142da925c52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Tue, 16 Jan 2024 18:59:38 +0900 Subject: [PATCH 03/89] =?UTF-8?q?chore(application.yml)=20:=20JWT,Validati?= =?UTF-8?q?on,=20MySQL=20=EC=82=AC=EC=9A=A9=EC=9D=84=20=EC=9C=84=ED=95=9C?= =?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 --- build.gradle | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/build.gradle b/build.gradle index 7d6c47c2..bea61724 100644 --- a/build.gradle +++ b/build.gradle @@ -20,8 +20,23 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + + //SPRING_REST_DOCS asciidoctor 'org.springframework.restdocs:spring-restdocs-asciidoctor' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' + + //JWT + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + + //VALIDATION + implementation 'org.springframework.boot:spring-boot-starter-validation' + + //MYSQL + runtimeOnly 'com.mysql:mysql-connector-j' + } ext { From cca9525d5b2b507fb6501a7558785cc4e222ba55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Tue, 16 Jan 2024 19:00:39 +0900 Subject: [PATCH 04/89] =?UTF-8?q?test(AuthDocTest)=20:=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8,=20=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83,=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=9E=AC=EB=B0=9C=EA=B8=89=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20controller=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/aliens/backend/docs/AuthDocTest.java | 51 ++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/aliens/backend/docs/AuthDocTest.java b/src/test/java/com/aliens/backend/docs/AuthDocTest.java index bba4f61d..ee174af0 100644 --- a/src/test/java/com/aliens/backend/docs/AuthDocTest.java +++ b/src/test/java/com/aliens/backend/docs/AuthDocTest.java @@ -17,8 +17,7 @@ import static org.mockito.Mockito.when; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @AutoConfigureMockMvc @@ -47,6 +46,54 @@ void login() throws Exception { requestFields( fieldWithPath("email").description("이메일"), fieldWithPath("password").description("비밀번호") + ), + + responseFields( + fieldWithPath("accessToken").description("발급된 엑세스토큰"), + fieldWithPath("refreshToken").description("발급된 리프레쉬토큰") + ) + )); + } + + @Test + void logout() throws Exception { + final AuthToken request = new AuthToken("accessToken", "refreshToken"); + final String response = AuthService.LOGOUT_SUCCESS; + when(authService.logout(any())).thenReturn(response); + + ObjectMapper objectMapper = new ObjectMapper(); + this.mockMvc.perform(post("/authentication/logout") + .content(objectMapper.writeValueAsString(request)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andDo(document("auth-logout", + requestFields( + fieldWithPath("accessToken").description("엑세스토큰"), + fieldWithPath("refreshToken").description("리프레쉬토큰") + ) + )); + } + + @Test + void reissue() throws Exception { + final AuthToken request = new AuthToken("expiredAccessToken", "refreshToken"); + final AuthToken response = new AuthToken("newAccessToken", "refreshToken"); + when(authService.reissue(any())).thenReturn(response); + + ObjectMapper objectMapper = new ObjectMapper(); + this.mockMvc.perform(post("/authentication/reissue") + .content(objectMapper.writeValueAsString(request)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andDo(document("auth-reissue", + requestFields( + fieldWithPath("accessToken").description("만료된 엑세스토큰"), + fieldWithPath("refreshToken").description("재발급을 위한 리프레쉬토큰") + ), + + responseFields( + fieldWithPath("accessToken").description("새로 발급된 엑세스토큰"), + fieldWithPath("refreshToken").description("기존의 리프레쉬토큰") ) )); } From bfe6dc0e6b9f82227c01a9a8281cb1c304b6998c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Tue, 16 Jan 2024 19:01:09 +0900 Subject: [PATCH 05/89] =?UTF-8?q?feat(AuthController)=20:=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8,=20=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83,=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=9E=AC=EB=B0=9C=EA=B8=89=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20controller=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/auth/controller/AuthController.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/aliens/backend/auth/controller/AuthController.java b/src/main/java/com/aliens/backend/auth/controller/AuthController.java index de915c3a..5d1dd941 100644 --- a/src/main/java/com/aliens/backend/auth/controller/AuthController.java +++ b/src/main/java/com/aliens/backend/auth/controller/AuthController.java @@ -10,7 +10,6 @@ @RequestMapping("/authentication") @RestController public class AuthController { - private final AuthService authService; public AuthController(final AuthService authService) { @@ -22,4 +21,16 @@ public ResponseEntity login(@RequestBody final LoginRequest loginRequ AuthToken authToken = authService.login(loginRequest); return ResponseEntity.ok(authToken); } + + @PostMapping("/logout") + public ResponseEntity logout(@RequestBody final AuthToken authToken) { + String result = authService.logout(authToken); + return ResponseEntity.ok(result); + } + + @PostMapping("/reissue") + public ResponseEntity reissue(@RequestBody final AuthToken authToken) { + AuthToken newAuthToken = authService.reissue(authToken); + return ResponseEntity.ok(newAuthToken); + } } \ No newline at end of file From a575bd40874db6f0abeed1d36c378c730ed8f399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Tue, 16 Jan 2024 19:02:08 +0900 Subject: [PATCH 06/89] =?UTF-8?q?feat(AuthService)=20:=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=84=B1=EA=B3=B5=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20=EC=9E=84=EC=8B=9C=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/aliens/backend/auth/service/AuthService.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/com/aliens/backend/auth/service/AuthService.java b/src/main/java/com/aliens/backend/auth/service/AuthService.java index 046c41d5..9622d2d3 100644 --- a/src/main/java/com/aliens/backend/auth/service/AuthService.java +++ b/src/main/java/com/aliens/backend/auth/service/AuthService.java @@ -6,7 +6,17 @@ @Service public class AuthService { + public static final String LOGOUT_SUCCESS = "로그아웃 되었습니다."; + public AuthToken login(LoginRequest loginRequest) { return new AuthToken("accessToken", "refreshToken"); } + + public String logout(final AuthToken authToken) { + return LOGOUT_SUCCESS; + } + + public AuthToken reissue(final AuthToken authToken) { + return new AuthToken("accessToken", "refreshToken"); + } } From a24856ec8252274e05968ac83f5f611cd4ee8a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Tue, 16 Jan 2024 19:02:44 +0900 Subject: [PATCH 07/89] =?UTF-8?q?docs(index.adoc)=20:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8,=20=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83,=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EC=9E=AC=EB=B0=9C=EA=B8=89=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20API=20=EB=AC=B8=EC=84=9C=ED=99=94=20=EC=9E=91?= =?UTF-8?q?=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/index.adoc | 46 +++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index 5f6e3acd..0f7c3b11 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -4,15 +4,55 @@ :sectlinks: [[resources-post]] -== Post +== Authentication [[resources-post-create]] -=== Post 생성 +=== 로그인 시도 ==== HTTP request include::{snippets}/auth-login/http-request.adoc[] +==== request-body 설명 +include::{snippets}/auth-login/request-fields.adoc[] + +==== HTTP response + +include::{snippets}/auth-login/http-response.adoc[] + +==== response-body 설명 +include::{snippets}/auth-login/response-fields.adoc[] + + + +=== 로그아웃 + +==== HTTP request + +include::{snippets}/auth-logout/http-request.adoc[] + +==== request-body 설명 +include::{snippets}/auth-logout/request-fields.adoc[] + ==== HTTP response -include::{snippets}/auth-login/http-response.adoc[] \ No newline at end of file +include::{snippets}/auth-logout/http-response.adoc[] + + + + +=== 토큰재발급 + +==== HTTP request + +include::{snippets}/auth-reissue/http-request.adoc[] + +==== request-body 설명 +include::{snippets}/auth-reissue/request-fields.adoc[] + +==== HTTP response + +include::{snippets}/auth-reissue/http-response.adoc[] + +==== response-body 설명 +include::{snippets}/auth-reissue/response-fields.adoc[] \ No newline at end of file From 821a103ad69308cf0f04769671420adccd92ff26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Tue, 16 Jan 2024 19:18:08 +0900 Subject: [PATCH 08/89] =?UTF-8?q?test(AuthServiceTest)=20:=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8,=20=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83,=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=9E=AC=EB=B0=9C=EA=B8=89=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/auth/service/AuthServiceTest.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java diff --git a/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java b/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java new file mode 100644 index 00000000..58389954 --- /dev/null +++ b/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java @@ -0,0 +1,62 @@ +package com.aliens.backend.auth.service; + +import com.aliens.backend.JWTProperties; +import com.aliens.backend.auth.controller.dto.AuthToken; +import com.aliens.backend.auth.controller.dto.LoginRequest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AuthServiceTest { + + @Autowired + AuthService authService; + @Autowired + JWTProperties jwtProperties; + + @Test + @DisplayName("로그인 성공") + void loginTest() { + //Given + LoginRequest loginRequest = new LoginRequest("tmp@example.com", "tmpPassword"); + + //When + AuthToken response = authService.login(loginRequest); + + //Then + Assertions.assertNotNull(response); + } + + @Test + @DisplayName("로그아웃 성공") + void logoutTest() { + //Given + LoginRequest loginRequest = new LoginRequest("tmp@example.com", "tmpPassword"); + AuthToken authToken = authService.login(loginRequest); + + //When + String response = authService.logout(authToken); + + //Then + Assertions.assertEquals(response, AuthService.LOGOUT_SUCCESS); + } + + @Test + @DisplayName("토큰 재발급 성공") + void reissueTest() { + //Given + LoginRequest loginRequest = new LoginRequest("tmp@example.com", "tmpPassword"); + jwtProperties.setAccessTokenValidTime(1L); //AccessToken 유효기한 짧게변경 + AuthToken requestAuthToken = authService.login(loginRequest); + jwtProperties.setAccessTokenValidTime(86400000L); //AccessToken 유효기한 원상복구 + + //When + AuthToken responseAuthToken = authService.reissue(requestAuthToken); + + //Then + Assertions.assertNotEquals(requestAuthToken, responseAuthToken); + } +} From a2bb0fad456c048bfac88fe46b27db8e3c5a488a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Tue, 16 Jan 2024 19:18:38 +0900 Subject: [PATCH 09/89] =?UTF-8?q?refactor(AuthService)=20:=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=EC=9E=AC=EB=B0=9C=EA=B8=89=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=84=B1=EA=B3=B5=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/aliens/backend/auth/service/AuthService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/aliens/backend/auth/service/AuthService.java b/src/main/java/com/aliens/backend/auth/service/AuthService.java index 9622d2d3..10e8c302 100644 --- a/src/main/java/com/aliens/backend/auth/service/AuthService.java +++ b/src/main/java/com/aliens/backend/auth/service/AuthService.java @@ -17,6 +17,6 @@ public String logout(final AuthToken authToken) { } public AuthToken reissue(final AuthToken authToken) { - return new AuthToken("accessToken", "refreshToken"); + return new AuthToken("newAccessToken", "refreshToken"); } } From 8d0879a46adb17bcb693d36e7fb251e24b8e32dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Tue, 16 Jan 2024 19:19:06 +0900 Subject: [PATCH 10/89] =?UTF-8?q?feat(JWTProperties)=20:=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=EC=9E=AC=EB=B0=9C=EA=B8=89=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=84=B1=EA=B3=B5=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/aliens/backend/JWTProperties.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/com/aliens/backend/JWTProperties.java diff --git a/src/main/java/com/aliens/backend/JWTProperties.java b/src/main/java/com/aliens/backend/JWTProperties.java new file mode 100644 index 00000000..3ec9d425 --- /dev/null +++ b/src/main/java/com/aliens/backend/JWTProperties.java @@ -0,0 +1,9 @@ +package com.aliens.backend; + +import org.springframework.stereotype.Component; + +@Component +public class JWTProperties { + public void setAccessTokenValidTime(final long i) { + } +} From 39b67b45e18c83c54d8365193cc24105600740a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 17 Jan 2024 00:06:15 +0900 Subject: [PATCH 11/89] =?UTF-8?q?refactor(JWTProperties)=20:=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=A0=95=EB=B3=B4=EB=A5=BC=20=EA=B0=96=EB=8A=94=20?= =?UTF-8?q?=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EB=B0=8F=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/aliens/backend/{ => global/property}/JWTProperties.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/com/aliens/backend/{ => global/property}/JWTProperties.java (77%) diff --git a/src/main/java/com/aliens/backend/JWTProperties.java b/src/main/java/com/aliens/backend/global/property/JWTProperties.java similarity index 77% rename from src/main/java/com/aliens/backend/JWTProperties.java rename to src/main/java/com/aliens/backend/global/property/JWTProperties.java index 3ec9d425..394ea7c6 100644 --- a/src/main/java/com/aliens/backend/JWTProperties.java +++ b/src/main/java/com/aliens/backend/global/property/JWTProperties.java @@ -1,4 +1,4 @@ -package com.aliens.backend; +package com.aliens.backend.global.property; import org.springframework.stereotype.Component; From a567867d944bd15fd88790cca6d91cf57488581a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 17 Jan 2024 00:06:53 +0900 Subject: [PATCH 12/89] =?UTF-8?q?feat(ErrorCode)=20:=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=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 --- .../java/com/aliens/backend/global/error/ErrorCode.java | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/error/ErrorCode.java diff --git a/src/main/java/com/aliens/backend/global/error/ErrorCode.java b/src/main/java/com/aliens/backend/global/error/ErrorCode.java new file mode 100644 index 00000000..0e73ed63 --- /dev/null +++ b/src/main/java/com/aliens/backend/global/error/ErrorCode.java @@ -0,0 +1,6 @@ +package com.aliens.backend.global.error; + +public interface ErrorCode { + String getCode(); + String getMessage(); +} From abc08740a9cd8eaef2281594f6852d725aaf3acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 17 Jan 2024 00:08:46 +0900 Subject: [PATCH 13/89] =?UTF-8?q?feat(ErrorResponse)=20:=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EC=BD=94=EB=93=9C=EB=A5=BC=20=EA=B0=96=EB=8A=94=20Rep?= =?UTF-8?q?onse=20=ED=81=B4=EB=9E=98=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 --- .../global/exception/ErrorResponse.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/exception/ErrorResponse.java diff --git a/src/main/java/com/aliens/backend/global/exception/ErrorResponse.java b/src/main/java/com/aliens/backend/global/exception/ErrorResponse.java new file mode 100644 index 00000000..2cabc7ce --- /dev/null +++ b/src/main/java/com/aliens/backend/global/exception/ErrorResponse.java @@ -0,0 +1,34 @@ +package com.aliens.backend.global.exception; + +import com.aliens.backend.global.error.ErrorCode; + +public class ErrorResponse { + + private final String code; + private final String message; + + private ErrorResponse(final String code, final String message) { + this.code = code; + this.message = message; + } + + static ErrorResponse from(final ErrorCode errorCode) { + return new ErrorResponse(errorCode.getCode(), errorCode.getMessage()); + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } + + @Override + public String toString() { + return "ErrorResponse{" + + "code='" + code + '\'' + + ", message='" + message + '\'' + + '}'; + } +} From 4e74e56d8da740632a230931df1438c3039aa003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 17 Jan 2024 00:09:10 +0900 Subject: [PATCH 14/89] =?UTF-8?q?feat(MemberError)=20:=20Member=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=EC=97=90=EC=84=9C=20=EC=9D=BC?= =?UTF-8?q?=EC=96=B4=EB=82=98=EB=8A=94=20=EC=97=90=EB=9F=AC=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/error/MemberError.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/error/MemberError.java diff --git a/src/main/java/com/aliens/backend/global/error/MemberError.java b/src/main/java/com/aliens/backend/global/error/MemberError.java new file mode 100644 index 00000000..b4236fb6 --- /dev/null +++ b/src/main/java/com/aliens/backend/global/error/MemberError.java @@ -0,0 +1,24 @@ +package com.aliens.backend.global.error; + +public enum MemberError implements ErrorCode{ + + INVALID_PASSWORD("M1", "올바르지 않은 형식의 비밀번호"), + INVALID_EMAIL("M2", "올바르지 않은 형식의 이메일"), + NULL_MEMBER("M3", "해당 Member 엔티티 조회 불가"); + + private final String code; + private final String message; + + MemberError(final String code, final String message) { + this.code = code; + this.message = message; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } +} \ No newline at end of file From e481509c044f02aa69798b7dcc361c9966060d20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 17 Jan 2024 00:09:34 +0900 Subject: [PATCH 15/89] =?UTF-8?q?feat(TokenError)=20:=20Authentication?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=9D=BC=EC=96=B4=EB=82=98=EB=8A=94=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=97=90=EB=9F=AC=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/error/TokenError.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/error/TokenError.java diff --git a/src/main/java/com/aliens/backend/global/error/TokenError.java b/src/main/java/com/aliens/backend/global/error/TokenError.java new file mode 100644 index 00000000..b615c8ed --- /dev/null +++ b/src/main/java/com/aliens/backend/global/error/TokenError.java @@ -0,0 +1,26 @@ +package com.aliens.backend.global.error; + +public enum TokenError implements ErrorCode { + + INVALID_TOKEN("T1", "올바르지 않은 AccessToken"), + EXPIRED_ACCESS_TOKEN("T2", "만료된 AccessToken"), + EXPIRED_REFRESH_TOKEN("T3", "만료된 ReFreshToken"), + NULL_REFRESH_TOKEN("T4", "존재하지 않은 ReFreshToken 접근"), + NOT_ACCESS_TOKEN_FOR_REISSUE("T5","재발급하기에는 유효기간이 남은 AccessToken"); + + private final String code; + private final String message; + + TokenError(final String code, final String message) { + this.code = code; + this.message = message; + } + + public String getCode() { + return code; + } + + public String getMessage() { + return message; + } +} From 08aca5c0d032865e0050d07456ad3aabe754dca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 17 Jan 2024 00:10:10 +0900 Subject: [PATCH 16/89] =?UTF-8?q?feat(BaseException)=20:=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20Response=20=EB=A5=BC=20=EA=B0=96=EB=8A=94=20Runtime?= =?UTF-8?q?Exception=20=EA=B5=AC=ED=98=84=EC=B2=B4=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/exception/BaseException.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/exception/BaseException.java diff --git a/src/main/java/com/aliens/backend/global/exception/BaseException.java b/src/main/java/com/aliens/backend/global/exception/BaseException.java new file mode 100644 index 00000000..d60ecbdd --- /dev/null +++ b/src/main/java/com/aliens/backend/global/exception/BaseException.java @@ -0,0 +1,20 @@ +package com.aliens.backend.global.exception; + +import com.aliens.backend.global.error.ErrorCode; + +public class BaseException extends RuntimeException { + private final ErrorResponse errorResponse; + + public BaseException(final ErrorCode code) { + this.errorResponse = ErrorResponse.from(code); + } + + public ErrorResponse getErrorResponse() { + return errorResponse; + } + + @Override + public String getMessage() { + return errorResponse.getMessage(); + } +} \ No newline at end of file From 06e7110e02ce72afd4bf6fb2e4147f6f06f42fb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 17 Jan 2024 00:11:09 +0900 Subject: [PATCH 17/89] =?UTF-8?q?refactor(AuthServiceTest)=20:=20JWTProper?= =?UTF-8?q?ties=20=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20import?= =?UTF-8?q?=EB=AC=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/aliens/backend/auth/service/AuthServiceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java b/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java index 58389954..d58e291f 100644 --- a/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java +++ b/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java @@ -1,8 +1,8 @@ package com.aliens.backend.auth.service; -import com.aliens.backend.JWTProperties; import com.aliens.backend.auth.controller.dto.AuthToken; import com.aliens.backend.auth.controller.dto.LoginRequest; +import com.aliens.backend.global.property.JWTProperties; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; From eb856ffd4a46eb3f83d95324c2131d8ba51f1a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 17 Jan 2024 00:15:26 +0900 Subject: [PATCH 18/89] =?UTF-8?q?feat(JWTProperties)=20:=20=EB=B3=80?= =?UTF-8?q?=EC=88=98=20=EB=B0=8F=20get=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/property/JWTProperties.java | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/aliens/backend/global/property/JWTProperties.java b/src/main/java/com/aliens/backend/global/property/JWTProperties.java index 394ea7c6..aa98dad7 100644 --- a/src/main/java/com/aliens/backend/global/property/JWTProperties.java +++ b/src/main/java/com/aliens/backend/global/property/JWTProperties.java @@ -1,9 +1,39 @@ package com.aliens.backend.global.property; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import java.nio.charset.StandardCharsets; + @Component public class JWTProperties { - public void setAccessTokenValidTime(final long i) { + @Value("${jwt.secret_key}") + private String secretKey; + + @Value("${jwt.access_token_valid_time}") + private long accessTokenValidTime; + + @Value("${jwt.refresh_token_valid_time}") + private long refreshTokenValidTime ; + + @Value("${jwt.chat_token_valid_time}") + private long chatTokenValidTime ; + + + public byte[] getBytesSecretKey() { + return secretKey.getBytes(StandardCharsets.UTF_8); + } + + public long getRefreshTokenValidTime() { + return refreshTokenValidTime; + } + + public long getAccessTokenValidTime() { + return accessTokenValidTime; + } + + public void setAccessTokenValidTime(Long time) { + accessTokenValidTime = time; } } + From 929a0c279a4cdf9e5f0b261820602e60f1f42abd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 17 Jan 2024 00:16:08 +0900 Subject: [PATCH 19/89] =?UTF-8?q?style(AuthServiceTest)=20:=20assertEquals?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=EC=97=90=20=EB=93=A4=EC=96=B4?= =?UTF-8?q?=EA=B0=80=EB=8A=94=20argument=20=EC=88=9C=EC=84=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/aliens/backend/auth/service/AuthServiceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java b/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java index d58e291f..89cf3dec 100644 --- a/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java +++ b/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java @@ -41,7 +41,7 @@ void logoutTest() { String response = authService.logout(authToken); //Then - Assertions.assertEquals(response, AuthService.LOGOUT_SUCCESS); + Assertions.assertEquals(AuthService.LOGOUT_SUCCESS, response); } @Test From c912b3221ad020765e34ff15b8281df1941cd8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 17 Jan 2024 00:26:04 +0900 Subject: [PATCH 20/89] =?UTF-8?q?feat(MemberException)=20:=20=EB=A9=A4?= =?UTF-8?q?=EB=B2=84=EC=99=80=20=EA=B4=80=EB=A0=A8=EB=90=9C=20ErrorRespons?= =?UTF-8?q?e=EB=A5=BC=20=EB=8B=B4=EC=9D=84=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/exception/MemberException.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/exception/MemberException.java diff --git a/src/main/java/com/aliens/backend/global/exception/MemberException.java b/src/main/java/com/aliens/backend/global/exception/MemberException.java new file mode 100644 index 00000000..a8ee01dd --- /dev/null +++ b/src/main/java/com/aliens/backend/global/exception/MemberException.java @@ -0,0 +1,20 @@ +package com.aliens.backend.global.exception; + +import com.aliens.backend.global.error.ErrorCode; + +public class MemberException extends RuntimeException{ + private final ErrorResponse errorResponse; + + public MemberException(final ErrorCode code) { + this.errorResponse = ErrorResponse.from(code); + } + + public ErrorResponse getErrorResponse() { + return errorResponse; + } + + @Override + public String getMessage() { + return errorResponse.getMessage(); + } +} \ No newline at end of file From f7094bf2e878c7d317995de4c52f792390ff027c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 17 Jan 2024 00:26:23 +0900 Subject: [PATCH 21/89] =?UTF-8?q?feat(TokenException)=20:=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=EA=B3=BC=20=EA=B4=80=EB=A0=A8=EB=90=9C=20ErrorRespons?= =?UTF-8?q?e=EB=A5=BC=20=EB=8B=B4=EC=9D=84=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/exception/TokenException.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/exception/TokenException.java diff --git a/src/main/java/com/aliens/backend/global/exception/TokenException.java b/src/main/java/com/aliens/backend/global/exception/TokenException.java new file mode 100644 index 00000000..3bce43b4 --- /dev/null +++ b/src/main/java/com/aliens/backend/global/exception/TokenException.java @@ -0,0 +1,20 @@ +package com.aliens.backend.global.exception; + +import com.aliens.backend.global.error.ErrorCode; + +public class TokenException extends RuntimeException{ + private final ErrorResponse errorResponse; + + public TokenException(final ErrorCode code) { + this.errorResponse = ErrorResponse.from(code); + } + + public ErrorResponse getErrorResponse() { + return errorResponse; + } + + @Override + public String getMessage() { + return errorResponse.getMessage(); + } +} \ No newline at end of file From 1ea69f3e815d04b3a4c580982c70e0c11f40de74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 17 Jan 2024 01:47:47 +0900 Subject: [PATCH 22/89] =?UTF-8?q?test(TokenProviderTest)=20:=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=EC=83=9D=EC=84=B1=EA=B3=BC=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EB=A9=A4=EB=B2=84=20=EC=B6=94=EC=B6=9C,=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EA=B2=80=EC=A6=9D=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/service/TokenProviderTest.java | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java diff --git a/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java b/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java new file mode 100644 index 00000000..7e32d845 --- /dev/null +++ b/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java @@ -0,0 +1,90 @@ +package com.aliens.backend.auth.service; + +import com.aliens.backend.auth.controller.dto.LoginMember; +import com.aliens.backend.auth.domain.MemberRole; +import com.aliens.backend.global.property.JWTProperties; +import org.junit.jupiter.api.Assertions; +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.boot.test.context.SpringBootTest; + +@SpringBootTest +class TokenProviderTest { + + @Autowired + private TokenProvider tokenProvider; + @Autowired + private JWTProperties jwtProperties; + + LoginMember givenLoginMember; + + @BeforeEach + void setUp() { + Long memberId = 1L; + MemberRole role = MemberRole.MEMBER; + givenLoginMember = new LoginMember(memberId,role); + } + + @Test + @DisplayName("엑세스토큰 생성") + void generateAccessTokenTest() { + //When + String accessToken = tokenProvider.generateAccessToken(givenLoginMember); + + //Then + Assertions.assertNotNull(accessToken); + } + + @Test + @DisplayName("리프레쉬토큰 생성") + void generateRefreshTokenTest() { + //When + String accessToken = tokenProvider.generateRefreshToken(givenLoginMember); + + //Then + Assertions.assertNotNull(accessToken); + } + + @Test + @DisplayName("토큰으로부터 로그인멤버 정보 추출") + void getLoginMemberFromAccessTokenTest() { + //Given + String accessToken = tokenProvider.generateAccessToken(givenLoginMember); + + //When + LoginMember result = tokenProvider.getLoginMemberFromToken(accessToken); + + //Then + Assertions.assertEquals(givenLoginMember,result); + } + + @Test + @DisplayName("만료된 토큰 검증") + void expiredTokenTest() { + //Given + jwtProperties.setAccessTokenValidTime(1L); //AccessToken 유효기한 짧게변경 + String accessToken = tokenProvider.generateAccessToken(givenLoginMember); + jwtProperties.setAccessTokenValidTime(86400000L); //AccessToken 유효기한 원상복구 + + //When + boolean result = tokenProvider.isNotExpiredToken(accessToken); + + //Then + Assertions.assertFalse(result); + } + + @Test + @DisplayName("기간이 유효한 토큰 검증") + void notExpiredTokenTest() { + //Given + String accessToken = tokenProvider.generateAccessToken(givenLoginMember); + + //When + boolean result = tokenProvider.isNotExpiredToken(accessToken); + + //Then + Assertions.assertTrue(result); + } +} From 64eb0a82126ba4af9f018d57df73dad5e91c3383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 17 Jan 2024 01:48:39 +0900 Subject: [PATCH 23/89] =?UTF-8?q?feat(MemberRole,=20LoginMember)=20:=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=EC=97=90=20=EC=A0=80=EC=9E=A5=EB=90=A0=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/controller/dto/LoginMember.java | 6 +++++ .../backend/auth/domain/MemberRole.java | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/main/java/com/aliens/backend/auth/controller/dto/LoginMember.java create mode 100644 src/main/java/com/aliens/backend/auth/domain/MemberRole.java diff --git a/src/main/java/com/aliens/backend/auth/controller/dto/LoginMember.java b/src/main/java/com/aliens/backend/auth/controller/dto/LoginMember.java new file mode 100644 index 00000000..1c75da8e --- /dev/null +++ b/src/main/java/com/aliens/backend/auth/controller/dto/LoginMember.java @@ -0,0 +1,6 @@ +package com.aliens.backend.auth.controller.dto; + +import com.aliens.backend.auth.domain.MemberRole; + +public record LoginMember(Long memberId, MemberRole role) { +} diff --git a/src/main/java/com/aliens/backend/auth/domain/MemberRole.java b/src/main/java/com/aliens/backend/auth/domain/MemberRole.java new file mode 100644 index 00000000..ee2000a9 --- /dev/null +++ b/src/main/java/com/aliens/backend/auth/domain/MemberRole.java @@ -0,0 +1,27 @@ +package com.aliens.backend.auth.domain; + +public enum MemberRole { + ADMIN(0,"관리자"), + MEMBER(1,"회원"); + + private final Integer code; + private final String description; + + MemberRole(final Integer code, final String description) { + this.code = code; + this.description = description; + } + + public static MemberRole fromCode(final Integer code) { + for (MemberRole role : values()) { + if (role.getCode().equals(code)) { + return role; + } + } + return null; + } + + public Integer getCode() { + return code; + } +} From d7b51d4b8109b3995d2193dc30a5910fb479accc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 17 Jan 2024 01:49:08 +0900 Subject: [PATCH 24/89] =?UTF-8?q?feat(TokenProvider)=20:=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EC=83=9D=EC=84=B1,=20=EA=B2=80=EC=A6=9D=EC=9D=84?= =?UTF-8?q?=20=EB=A7=A1=EC=9D=80=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/auth/service/TokenProvider.java | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/main/java/com/aliens/backend/auth/service/TokenProvider.java diff --git a/src/main/java/com/aliens/backend/auth/service/TokenProvider.java b/src/main/java/com/aliens/backend/auth/service/TokenProvider.java new file mode 100644 index 00000000..27f89a56 --- /dev/null +++ b/src/main/java/com/aliens/backend/auth/service/TokenProvider.java @@ -0,0 +1,83 @@ +package com.aliens.backend.auth.service; + +import com.aliens.backend.auth.controller.dto.LoginMember; +import com.aliens.backend.auth.domain.MemberRole; +import com.aliens.backend.global.property.JWTProperties; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; +import org.springframework.stereotype.Component; + +import java.util.Date; + +@Component +public class TokenProvider { + + private final JWTProperties jwtProperties; + + public TokenProvider(final JWTProperties jwtProperties) { + this.jwtProperties = jwtProperties; + } + + public String generateAccessToken(final LoginMember loginMember) { + Claims claims = getClaimsFrom(loginMember); + return getTokenFrom(claims,jwtProperties.getAccessTokenValidTime()); + } + + private Claims getClaimsFrom(final LoginMember loginMember) { + Claims claims = Jwts.claims(); + claims.put("memberId",loginMember.memberId()); + claims.put("role", loginMember.role().getCode()); + return claims; + } + + private String getTokenFrom(final Claims claims, final long validTime) { + Date now = new Date(); + return Jwts.builder() + .setHeaderParam("type", "JWT") + .setClaims(claims) + .setIssuedAt(now) + .setExpiration(new Date(now.getTime() + validTime)) + .signWith( + Keys.hmacShaKeyFor(jwtProperties.getBytesSecretKey()), + SignatureAlgorithm.HS256 + ) + .compact(); + } + + public String generateRefreshToken(final LoginMember loginMember) { + Claims claims = getClaimsFrom(loginMember); + return getTokenFrom(claims,jwtProperties.getRefreshTokenValidTime()); + } + + public LoginMember getLoginMemberFromToken(final String token) { + Claims claims = Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(jwtProperties.getBytesSecretKey())) + .build() + .parseClaimsJws(removeBearer(token)) + .getBody(); + + return new LoginMember(Long.parseLong(String.valueOf(claims.get("memberId"))), MemberRole.fromCode((Integer)claims.get("role"))); + } + + private String removeBearer(String token) { + if (token != null && token.startsWith("Authentication ")) { + return token.substring(7); + } + return token; + } + + public boolean isNotExpiredToken(final String token) { + try{ return !Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(jwtProperties.getBytesSecretKey())) + .build() + .parseClaimsJws(removeBearer(token)) + .getBody() + .getExpiration().before(new Date()); + } catch (ExpiredJwtException e) { + return false; + } + } +} From 3be0fdff292b5e6dfd070358872c4b0a36330246 Mon Sep 17 00:00:00 2001 From: Oniqued Date: Thu, 18 Jan 2024 21:49:15 +0900 Subject: [PATCH 25/89] =?UTF-8?q?chore=20:=20Asciidoctor=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98(asGemPath())=EB=A1=9C=20=EC=9D=B8=ED=95=B4=20build.gr?= =?UTF-8?q?adle=EC=9D=84=20=EC=88=98=EC=A0=95=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 10 +++++++--- gradlew | 0 2 files changed, 7 insertions(+), 3 deletions(-) mode change 100644 => 100755 gradlew diff --git a/build.gradle b/build.gradle index 7d6c47c2..e6c79093 100644 --- a/build.gradle +++ b/build.gradle @@ -2,13 +2,16 @@ plugins { id 'java' id 'org.springframework.boot' version '3.2.1' id 'io.spring.dependency-management' version '1.1.4' - id "org.asciidoctor.convert" version "1.5.9.2" - + id 'org.asciidoctor.jvm.convert' version "3.3.2" } group = 'com.aliens' version = '0.0.1-SNAPSHOT' +configurations { + asciidoctorExtensions +} + java { sourceCompatibility = '17' } @@ -20,7 +23,7 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' - asciidoctor 'org.springframework.restdocs:spring-restdocs-asciidoctor' + asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' } @@ -35,6 +38,7 @@ test { asciidoctor { inputs.dir snippetsDir dependsOn test + configurations 'asciidoctorExtensions' } bootJar { diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From c6078018484305c6bbc0866f6c4fa3c778034731 Mon Sep 17 00:00:00 2001 From: Oniqued Date: Thu, 18 Jan 2024 21:59:35 +0900 Subject: [PATCH 26/89] =?UTF-8?q?chore=20:=20=ED=99=98=EA=B2=BD=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=20yml=20=ED=8C=8C=EC=9D=BC=EC=9D=B4=EB=A6=84?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20-x=20test=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/friendship-pipeline.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/friendship-pipeline.yml b/.github/workflows/friendship-pipeline.yml index 4b1e0680..b2dad45b 100644 --- a/.github/workflows/friendship-pipeline.yml +++ b/.github/workflows/friendship-pipeline.yml @@ -25,17 +25,17 @@ jobs: distribution: 'temurin' # 설정 파일 추가 - - name: make application-prod.yml + - name: make application-secret.yml run: | cd ./src/main/resources - touch ./application-prod.yml - echo "${{ secrets.APPLICATION_PROD }}" > ./application-prod.yml + touch ./application-secret.yml + echo "${{ secrets.APPLICATION_SECRET }}" > ./application-secret.yml - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build with Gradle - run: ./gradlew build -x test + run: ./gradlew build - name: Docker build run: | From 08dce08e9573caac1a7ce425e8b7512543b9b2e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:28:49 +0900 Subject: [PATCH 27/89] =?UTF-8?q?test(TokenProviderTest)=20:=20=EB=A6=AC?= =?UTF-8?q?=ED=94=84=EB=A0=88=EC=8B=9C=ED=86=A0=ED=81=B0=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=EB=B6=80=ED=84=B0=20=ED=86=A0=ED=81=B0=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EB=94=94=20=EC=B6=94=EC=B6=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/service/TokenProviderTest.java | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java b/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java index 7e32d845..2dc807e5 100644 --- a/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java +++ b/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java @@ -18,11 +18,13 @@ class TokenProviderTest { @Autowired private JWTProperties jwtProperties; - LoginMember givenLoginMember; + private LoginMember givenLoginMember; + private Long givenTokenId; @BeforeEach void setUp() { Long memberId = 1L; + givenTokenId = 1L; MemberRole role = MemberRole.MEMBER; givenLoginMember = new LoginMember(memberId,role); } @@ -41,10 +43,10 @@ void generateAccessTokenTest() { @DisplayName("리프레쉬토큰 생성") void generateRefreshTokenTest() { //When - String accessToken = tokenProvider.generateRefreshToken(givenLoginMember); + String refreshToken = tokenProvider.generateRefreshToken(givenLoginMember,givenTokenId); //Then - Assertions.assertNotNull(accessToken); + Assertions.assertNotNull(refreshToken); } @Test @@ -87,4 +89,17 @@ void notExpiredTokenTest() { //Then Assertions.assertTrue(result); } + + @Test + @DisplayName("리프레시토큰으로부터 토큰 아이디 추출") + void getTokenIdFromTokenTest() { + //Given + String refreshToken = tokenProvider.generateRefreshToken(givenLoginMember,givenTokenId); + + //When + Long result = tokenProvider.getTokenIdFromToken(refreshToken); + + //Then + Assertions.assertEquals(result, givenTokenId); + } } From ef4c5382811b89d1746433eafd4c384da2ddf467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:29:22 +0900 Subject: [PATCH 28/89] =?UTF-8?q?feat(TokenProvider)=20:=20=EB=A6=AC?= =?UTF-8?q?=ED=94=84=EB=A0=88=EC=8B=9C=ED=86=A0=ED=81=B0=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=EB=B6=80=ED=84=B0=20=ED=86=A0=ED=81=B0=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EB=94=94=20=EC=B6=94=EC=B6=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/auth/service/TokenProvider.java | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/aliens/backend/auth/service/TokenProvider.java b/src/main/java/com/aliens/backend/auth/service/TokenProvider.java index 27f89a56..73341bf5 100644 --- a/src/main/java/com/aliens/backend/auth/service/TokenProvider.java +++ b/src/main/java/com/aliens/backend/auth/service/TokenProvider.java @@ -47,22 +47,30 @@ private String getTokenFrom(final Claims claims, final long validTime) { .compact(); } - public String generateRefreshToken(final LoginMember loginMember) { - Claims claims = getClaimsFrom(loginMember); + public String generateRefreshToken(final LoginMember loginMember, final Long tokenId) { + Claims claims = getClaimsFrom(loginMember,tokenId); return getTokenFrom(claims,jwtProperties.getRefreshTokenValidTime()); } + private Claims getClaimsFrom(final LoginMember loginMember, final Long tokenId) { + Claims claims = Jwts.claims(); + claims.put("memberId",loginMember.memberId()); + claims.put("role", loginMember.role().getCode()); + claims.put("tokenId", tokenId); + return claims; + } + public LoginMember getLoginMemberFromToken(final String token) { Claims claims = Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(jwtProperties.getBytesSecretKey())) .build() - .parseClaimsJws(removeBearer(token)) + .parseClaimsJws(removePrefix(token)) .getBody(); - return new LoginMember(Long.parseLong(String.valueOf(claims.get("memberId"))), MemberRole.fromCode((Integer)claims.get("role"))); + return new LoginMember(Long.parseLong(String.valueOf(claims.get("memberId"))), MemberRole.of((Integer)claims.get("role"))); } - private String removeBearer(String token) { + private String removePrefix(String token) { if (token != null && token.startsWith("Authentication ")) { return token.substring(7); } @@ -73,11 +81,21 @@ public boolean isNotExpiredToken(final String token) { try{ return !Jwts.parserBuilder() .setSigningKey(Keys.hmacShaKeyFor(jwtProperties.getBytesSecretKey())) .build() - .parseClaimsJws(removeBearer(token)) + .parseClaimsJws(removePrefix(token)) .getBody() .getExpiration().before(new Date()); } catch (ExpiredJwtException e) { return false; } } + + public Long getTokenIdFromToken(final String refreshToken) { + Claims claims = Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(jwtProperties.getBytesSecretKey())) + .build() + .parseClaimsJws(removePrefix(refreshToken)) + .getBody(); + + return Long.parseLong(String.valueOf(claims.get("tokenId"))); + } } From 80ebe3db9f1663fdc336d79433fee814b75f4aae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:30:35 +0900 Subject: [PATCH 29/89] =?UTF-8?q?feat(MemberError)=20:=20=EB=B9=84?= =?UTF-8?q?=EB=B0=80=EB=B2=88=ED=98=B8=20=ED=8B=80=EB=A6=BC,=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=A1=B0=ED=9A=8C=20=EC=8B=A4=ED=8C=A8=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/aliens/backend/global/error/MemberError.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/aliens/backend/global/error/MemberError.java b/src/main/java/com/aliens/backend/global/error/MemberError.java index b4236fb6..9ee5808e 100644 --- a/src/main/java/com/aliens/backend/global/error/MemberError.java +++ b/src/main/java/com/aliens/backend/global/error/MemberError.java @@ -4,7 +4,9 @@ public enum MemberError implements ErrorCode{ INVALID_PASSWORD("M1", "올바르지 않은 형식의 비밀번호"), INVALID_EMAIL("M2", "올바르지 않은 형식의 이메일"), - NULL_MEMBER("M3", "해당 Member 엔티티 조회 불가"); + NULL_MEMBER("M3", "해당 Member 엔티티 조회 불가"), + INCORRECT_PASSWORD("M4", "비밀번호 틀림"), + ; private final String code; private final String message; From 0ed4a5b82187a1199729ee95e5568e63bea44ab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:31:38 +0900 Subject: [PATCH 30/89] =?UTF-8?q?Style(MemberRole)=20:=20fromCode=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EB=84=A4=EC=9D=B4=EB=B0=8D,=20?= =?UTF-8?q?=EB=B0=9B=EB=8A=94=20=EC=9A=94=EC=86=8C=20=ED=95=98=EB=82=98?= =?UTF-8?q?=EB=A1=9C=20MemberRole=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20?= =?UTF-8?q?=EC=A3=BC=EA=B8=B0=EC=97=90=20of=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/aliens/backend/auth/domain/MemberRole.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/aliens/backend/auth/domain/MemberRole.java b/src/main/java/com/aliens/backend/auth/domain/MemberRole.java index ee2000a9..bfbdc6e9 100644 --- a/src/main/java/com/aliens/backend/auth/domain/MemberRole.java +++ b/src/main/java/com/aliens/backend/auth/domain/MemberRole.java @@ -12,7 +12,7 @@ public enum MemberRole { this.description = description; } - public static MemberRole fromCode(final Integer code) { + public static MemberRole of(final Integer code) { for (MemberRole role : values()) { if (role.getCode().equals(code)) { return role; From 24cd5dd1658d67b9b91751add04555af719cf615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:32:12 +0900 Subject: [PATCH 31/89] =?UTF-8?q?feat(Member)=20:=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aliens/backend/auth/domain/Member.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/main/java/com/aliens/backend/auth/domain/Member.java diff --git a/src/main/java/com/aliens/backend/auth/domain/Member.java b/src/main/java/com/aliens/backend/auth/domain/Member.java new file mode 100644 index 00000000..3ec76785 --- /dev/null +++ b/src/main/java/com/aliens/backend/auth/domain/Member.java @@ -0,0 +1,50 @@ +package com.aliens.backend.auth.domain; + +import com.aliens.backend.auth.controller.dto.LoginMember; +import jakarta.persistence.*; + +import java.util.ArrayList; +import java.util.List; + + +@Entity +public class Member { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column + private Long id; + + @Column + private String email; + + @Column + private String password; + + @Column + private MemberRole role; + + @OneToMany(mappedBy = "member", cascade = CascadeType.REMOVE) + private List tokens = new ArrayList<>(); + + protected Member() { + } + + public Member(final String email, final String password, final MemberRole role) { + this.email = email; + this.password = password; + this.role = role; + } + + public boolean isCorrectPassword(String password) { + return this.password.equals(password); + } + + public LoginMember getLoginMember() { + return new LoginMember(id,role); + } + + @Override + public String toString() { + return String.format("email: %s, password : %s, role : %s", this.email, this.password, this.role); + } +} \ No newline at end of file From 1129b74a2ff459d11ce7d881c8ad4df4b538e25d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:32:26 +0900 Subject: [PATCH 32/89] =?UTF-8?q?feat(MemberRepository)=20:=20=EB=A9=A4?= =?UTF-8?q?=EB=B2=84=20=EB=A0=88=ED=8C=8C=EC=A7=80=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/domain/repository/MemberRepository.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/com/aliens/backend/auth/domain/repository/MemberRepository.java diff --git a/src/main/java/com/aliens/backend/auth/domain/repository/MemberRepository.java b/src/main/java/com/aliens/backend/auth/domain/repository/MemberRepository.java new file mode 100644 index 00000000..960c9431 --- /dev/null +++ b/src/main/java/com/aliens/backend/auth/domain/repository/MemberRepository.java @@ -0,0 +1,12 @@ +package com.aliens.backend.auth.domain.repository; + +import com.aliens.backend.auth.domain.Member; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface MemberRepository extends JpaRepository { + Optional findByEmail(String email); +} From 736c09c54a4e608e590c10b8bbfb8a8acef14cf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:32:41 +0900 Subject: [PATCH 33/89] =?UTF-8?q?feat(Token)=20:=20=ED=86=A0=ED=81=B0=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/aliens/backend/auth/domain/Token.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/main/java/com/aliens/backend/auth/domain/Token.java diff --git a/src/main/java/com/aliens/backend/auth/domain/Token.java b/src/main/java/com/aliens/backend/auth/domain/Token.java new file mode 100644 index 00000000..29ed999f --- /dev/null +++ b/src/main/java/com/aliens/backend/auth/domain/Token.java @@ -0,0 +1,53 @@ +package com.aliens.backend.auth.domain; + +import jakarta.persistence.*; + +import java.time.LocalDateTime; + +@Entity +public class Token { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "refresh_token") + private String refreshToken; + + @Column + private boolean isExpired = false; + + @Column + private LocalDateTime recentLogin = LocalDateTime.now(); + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + protected Token() { + } + + public Token(Member member) { + this.member = member; + } + + public void putRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + + public void expire() { + isExpired = true; + } + + public boolean isExpired() { + return isExpired; + } + + public Long getId() { + return id; + } + + public void changeRecentLogin() { + recentLogin = LocalDateTime.now(); + } +} \ No newline at end of file From 0de30c541cc7c023f4d305b3649efa54b3101e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:32:57 +0900 Subject: [PATCH 34/89] =?UTF-8?q?feat(TokenRepository)=20:=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=EB=A0=88=ED=8C=8C=EC=A7=80=ED=86=A0=EB=A6=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/auth/domain/repository/TokenRepository.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/com/aliens/backend/auth/domain/repository/TokenRepository.java diff --git a/src/main/java/com/aliens/backend/auth/domain/repository/TokenRepository.java b/src/main/java/com/aliens/backend/auth/domain/repository/TokenRepository.java new file mode 100644 index 00000000..395799d2 --- /dev/null +++ b/src/main/java/com/aliens/backend/auth/domain/repository/TokenRepository.java @@ -0,0 +1,9 @@ +package com.aliens.backend.auth.domain.repository; + +import com.aliens.backend.auth.domain.Token; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface TokenRepository extends JpaRepository { +} From 65eb57f70945e8827ded53e1aaecf361caea0c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:33:26 +0900 Subject: [PATCH 35/89] =?UTF-8?q?feat(PasswordEncoder)=20:=20=EB=B9=84?= =?UTF-8?q?=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=9D=B8=EC=BD=94=EB=94=A9?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/auth/service/PasswordEncoder.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/main/java/com/aliens/backend/auth/service/PasswordEncoder.java diff --git a/src/main/java/com/aliens/backend/auth/service/PasswordEncoder.java b/src/main/java/com/aliens/backend/auth/service/PasswordEncoder.java new file mode 100644 index 00000000..8bd82e17 --- /dev/null +++ b/src/main/java/com/aliens/backend/auth/service/PasswordEncoder.java @@ -0,0 +1,36 @@ +package com.aliens.backend.auth.service; + +import com.aliens.backend.global.error.MemberError; +import com.aliens.backend.global.exception.MemberException; +import com.aliens.backend.global.property.EncodeProperties; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Base64; + +@Component +public class PasswordEncoder { + + private final EncodeProperties encodeProperties; + + public PasswordEncoder(final EncodeProperties encodeProperties) { + this.encodeProperties = encodeProperties; + } + + public String encrypt(String input) { + try { + KeySpec spec = new PBEKeySpec(input.toCharArray(), encodeProperties.getKey(), 85319, 128); + SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); + + byte[] hash = factory.generateSecret(spec).getEncoded(); + + return Base64.getEncoder().encodeToString(hash); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new MemberException(MemberError.INVALID_PASSWORD); + } + } +} From f3ad34235cb382dbcd739e55b4e1bf14b00e75a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:33:42 +0900 Subject: [PATCH 36/89] =?UTF-8?q?feat(PasswordEncoder)=20:=20=EB=B9=84?= =?UTF-8?q?=EB=B0=80=EB=B2=88=ED=98=B8=20=EC=9D=B8=EC=BD=94=EB=94=A9?= =?UTF-8?q?=EC=97=90=20=EC=93=B0=EC=9D=B4=EB=8A=94=20=ED=82=A4=20=EA=B0=92?= =?UTF-8?q?=EC=9D=84=20=EA=B0=80=EC=A7=84=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/property/EncodeProperties.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/property/EncodeProperties.java diff --git a/src/main/java/com/aliens/backend/global/property/EncodeProperties.java b/src/main/java/com/aliens/backend/global/property/EncodeProperties.java new file mode 100644 index 00000000..8e431900 --- /dev/null +++ b/src/main/java/com/aliens/backend/global/property/EncodeProperties.java @@ -0,0 +1,17 @@ +package com.aliens.backend.global.property; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.nio.charset.StandardCharsets; + +@Component +public class EncodeProperties { + + @Value("${encode.key}") + private String secretKey; + + public byte[] getKey() { + return secretKey.getBytes(StandardCharsets.UTF_8); + } +} From c43a06a90b13185a68a783b199a1d8d8af09a434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:34:39 +0900 Subject: [PATCH 37/89] =?UTF-8?q?feat(AuthServiceTest)=20:=20=EC=8B=A4?= =?UTF-8?q?=EC=A0=9C=20DB=EC=97=90=EC=84=9C=20=EB=8F=99=EC=9E=91=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20setUp,=20afterDown=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/auth/service/AuthServiceTest.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java b/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java index 89cf3dec..48f24704 100644 --- a/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java +++ b/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java @@ -2,10 +2,11 @@ import com.aliens.backend.auth.controller.dto.AuthToken; import com.aliens.backend.auth.controller.dto.LoginRequest; +import com.aliens.backend.auth.domain.Member; +import com.aliens.backend.auth.domain.repository.MemberRepository; +import com.aliens.backend.auth.domain.MemberRole; import com.aliens.backend.global.property.JWTProperties; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -16,6 +17,25 @@ class AuthServiceTest { AuthService authService; @Autowired JWTProperties jwtProperties; + @Autowired + MemberRepository memberRepository; + @Autowired + PasswordEncoder passwordEncoder; + + Member member; + + @BeforeEach + void setUp() { + member = new Member("tmp@example.com", + passwordEncoder.encrypt("tmpPassword"), + MemberRole.MEMBER); + memberRepository.save(member); + } + + @AfterEach + void afterDown() { + memberRepository.deleteAll(); + } @Test @DisplayName("로그인 성공") From a0daa6e34ddf11ec5aed7e3b5eb67336c4d91e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Fri, 19 Jan 2024 22:35:03 +0900 Subject: [PATCH 38/89] =?UTF-8?q?refactor(AuthService)=20:=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8,=20=EB=A1=9C=EA=B7=B8=EC=95=84=EC=9B=83,=20?= =?UTF-8?q?=ED=86=A0=ED=81=B0=20=EC=9E=AC=EB=B0=9C=EA=B8=89=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/auth/service/AuthService.java | 93 ++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/aliens/backend/auth/service/AuthService.java b/src/main/java/com/aliens/backend/auth/service/AuthService.java index 10e8c302..e1646817 100644 --- a/src/main/java/com/aliens/backend/auth/service/AuthService.java +++ b/src/main/java/com/aliens/backend/auth/service/AuthService.java @@ -1,22 +1,111 @@ package com.aliens.backend.auth.service; import com.aliens.backend.auth.controller.dto.AuthToken; +import com.aliens.backend.auth.controller.dto.LoginMember; import com.aliens.backend.auth.controller.dto.LoginRequest; +import com.aliens.backend.auth.domain.*; +import com.aliens.backend.auth.domain.repository.MemberRepository; +import com.aliens.backend.auth.domain.repository.TokenRepository; +import com.aliens.backend.global.error.MemberError; +import com.aliens.backend.global.error.TokenError; +import com.aliens.backend.global.exception.MemberException; +import com.aliens.backend.global.exception.TokenException; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service public class AuthService { + + private final MemberRepository memberRepository; + private final TokenRepository tokenRepository; + private final PasswordEncoder passwordEncoder; + private final TokenProvider tokenProvider; + public static final String LOGOUT_SUCCESS = "로그아웃 되었습니다."; + public AuthService(final MemberRepository memberRepository, + final TokenRepository tokenRepository, + final PasswordEncoder passwordEncoder, + final TokenProvider tokenProvider) { + this.memberRepository = memberRepository; + this.tokenRepository = tokenRepository; + this.passwordEncoder = passwordEncoder; + this.tokenProvider = tokenProvider; + } + + @Transactional public AuthToken login(LoginRequest loginRequest) { - return new AuthToken("accessToken", "refreshToken"); + Member member = getMemberEntityFromEmail(loginRequest.email()); + passwordCheck(loginRequest.password(), member); + return generateAuthToken(member); + } + + private Member getMemberEntityFromEmail(final String email) { + return memberRepository.findByEmail(email).orElseThrow(() -> new MemberException(MemberError.NULL_MEMBER)); + } + + private void passwordCheck(final String password, final Member member) { + if(!member.isCorrectPassword(passwordEncoder.encrypt(password))) { + throw new MemberException(MemberError.INCORRECT_PASSWORD); + } + } + + private AuthToken generateAuthToken(final Member member) { + LoginMember loginMember = member.getLoginMember(); + + String accessToken = tokenProvider.generateAccessToken(loginMember); + String refreshToken = generateRefreshToken(member, loginMember); + + return new AuthToken(accessToken,refreshToken); } + private String generateRefreshToken(final Member member, final LoginMember loginMember) { + Token token = new Token(member); + tokenRepository.save(token); + String refreshToken = tokenProvider.generateRefreshToken(loginMember,token.getId()); + + token.putRefreshToken(refreshToken); + return refreshToken; + } + + @Transactional public String logout(final AuthToken authToken) { + Long tokenId = tokenProvider.getTokenIdFromToken(authToken.refreshToken()); + Token token = getTokenEntity(tokenId); + token.expire(); return LOGOUT_SUCCESS; } + private Token getTokenEntity(final Long tokenId) { + return tokenRepository.findById(tokenId).orElseThrow(() -> new TokenException(TokenError.NULL_REFRESH_TOKEN)); + } + + @Transactional public AuthToken reissue(final AuthToken authToken) { - return new AuthToken("newAccessToken", "refreshToken"); + tokenExpiredCheck(authToken); + LoginMember loginMember = tokenProvider.getLoginMemberFromToken(authToken.refreshToken()); + String newAccessToken = tokenProvider.generateAccessToken(loginMember); + return new AuthToken(newAccessToken, authToken.refreshToken()); + } + + private void tokenExpiredCheck(final AuthToken authToken) { + if (tokenProvider.isNotExpiredToken(authToken.accessToken())) { + throw new TokenException(TokenError.NOT_ACCESS_TOKEN_FOR_REISSUE); + } + if (!tokenProvider.isNotExpiredToken(authToken.refreshToken())) { + throw new TokenException(TokenError.EXPIRED_REFRESH_TOKEN); + } + + dbTokenExpiredCheck(authToken.refreshToken()); + } + + private void dbTokenExpiredCheck(final String refreshToken) { + Long tokenId = tokenProvider.getTokenIdFromToken(refreshToken); + Token token = getTokenEntity(tokenId); + + if (token.isExpired()) { + throw new TokenException(TokenError.EXPIRED_REFRESH_TOKEN); + } + token.changeRecentLogin(); } } From 993500df3793c92eefa534379f56c3546f915585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 14:45:54 +0900 Subject: [PATCH 39/89] =?UTF-8?q?refactor(AuthDocTest)=20:=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=84=A4=EB=AA=85?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9CDisplayName=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/com/aliens/backend/docs/AuthDocTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/java/com/aliens/backend/docs/AuthDocTest.java b/src/test/java/com/aliens/backend/docs/AuthDocTest.java index ee174af0..c612434c 100644 --- a/src/test/java/com/aliens/backend/docs/AuthDocTest.java +++ b/src/test/java/com/aliens/backend/docs/AuthDocTest.java @@ -4,6 +4,7 @@ import com.aliens.backend.auth.controller.dto.LoginRequest; import com.aliens.backend.auth.service.AuthService; import com.fasterxml.jackson.databind.ObjectMapper; +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.restdocs.AutoConfigureRestDocs; @@ -32,6 +33,7 @@ class AuthDocTest { private AuthService authService; @Test + @DisplayName("로그인 API 테스트") void login() throws Exception { final LoginRequest request = new LoginRequest("email","password"); final AuthToken response = new AuthToken("accessToken", "refreshToken"); @@ -56,6 +58,7 @@ void login() throws Exception { } @Test + @DisplayName("로그아웃 API 테스트") void logout() throws Exception { final AuthToken request = new AuthToken("accessToken", "refreshToken"); final String response = AuthService.LOGOUT_SUCCESS; @@ -75,6 +78,7 @@ void logout() throws Exception { } @Test + @DisplayName("토큰 재발급 API 테스트") void reissue() throws Exception { final AuthToken request = new AuthToken("expiredAccessToken", "refreshToken"); final AuthToken response = new AuthToken("newAccessToken", "refreshToken"); From c6ddb2c39fde4277283b074736453b728f37b7b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 14:46:25 +0900 Subject: [PATCH 40/89] =?UTF-8?q?test(AuthServiceTest)=20:=20=EC=8B=A4?= =?UTF-8?q?=ED=8C=A8=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/auth/service/AuthServiceTest.java | 53 ++++++++++++++++--- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java b/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java index 48f24704..986f20fd 100644 --- a/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java +++ b/src/test/java/com/aliens/backend/auth/service/AuthServiceTest.java @@ -5,8 +5,11 @@ import com.aliens.backend.auth.domain.Member; import com.aliens.backend.auth.domain.repository.MemberRepository; import com.aliens.backend.auth.domain.MemberRole; +import com.aliens.backend.global.exception.RestApiException; import com.aliens.backend.global.property.JWTProperties; import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -23,12 +26,16 @@ class AuthServiceTest { PasswordEncoder passwordEncoder; Member member; + String email; + String password; + LoginRequest loginRequest; @BeforeEach void setUp() { - member = new Member("tmp@example.com", - passwordEncoder.encrypt("tmpPassword"), - MemberRole.MEMBER); + email = "tmp@example.com"; + password = "tmpPassword"; + member = new Member(email, passwordEncoder.encrypt(password), MemberRole.MEMBER); + loginRequest = new LoginRequest(email, password); memberRepository.save(member); } @@ -40,9 +47,6 @@ void afterDown() { @Test @DisplayName("로그인 성공") void loginTest() { - //Given - LoginRequest loginRequest = new LoginRequest("tmp@example.com", "tmpPassword"); - //When AuthToken response = authService.login(loginRequest); @@ -50,11 +54,23 @@ void loginTest() { Assertions.assertNotNull(response); } + @DisplayName("로그인 실패 - 없는 회원, 비밀번호 불일치") + @ParameterizedTest + @CsvSource(value = {"noMember@example.com : password", + "tmp@example.com : incorrectPassword"}, + delimiter = ':') + void loginFailTest(String givenEmail, String givenPassword) { + // Given + LoginRequest inValidLoginRequest = new LoginRequest(givenEmail, givenPassword); + + // When & Then + Assertions.assertThrows(RestApiException.class, () -> authService.login(inValidLoginRequest)); + } + @Test @DisplayName("로그아웃 성공") void logoutTest() { //Given - LoginRequest loginRequest = new LoginRequest("tmp@example.com", "tmpPassword"); AuthToken authToken = authService.login(loginRequest); //When @@ -68,7 +84,6 @@ void logoutTest() { @DisplayName("토큰 재발급 성공") void reissueTest() { //Given - LoginRequest loginRequest = new LoginRequest("tmp@example.com", "tmpPassword"); jwtProperties.setAccessTokenValidTime(1L); //AccessToken 유효기한 짧게변경 AuthToken requestAuthToken = authService.login(loginRequest); jwtProperties.setAccessTokenValidTime(86400000L); //AccessToken 유효기한 원상복구 @@ -79,4 +94,26 @@ void reissueTest() { //Then Assertions.assertNotEquals(requestAuthToken, responseAuthToken); } + + @Test + @DisplayName("토큰 재발급 실패 - 유효기간이 남은 AccessToken") + void reissueFailTestByAccessToken() { + //Given + AuthToken requestAuthToken = authService.login(loginRequest); + + //When & Then + Assertions.assertThrows(RestApiException.class, () -> authService.reissue(requestAuthToken)); + } + + @Test + @DisplayName("토큰 재발급 실패 - 만료된 RefreshToken") + void reissueFailTestByRefreshToken() { + //Given + jwtProperties.setRefreshTokenValidTime(1L); //RefreshToken 유효기한 짧게변경 + AuthToken requestAuthToken = authService.login(loginRequest); + jwtProperties.setRefreshTokenValidTime(2592000000L); //RefreshToken 유효기한 원상복구 + + //When & Then + Assertions.assertThrows(RestApiException.class, () -> authService.reissue(requestAuthToken)); + } } From 8fa985dd2fa66d1943215f9539724f81d09abd6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 14:47:22 +0900 Subject: [PATCH 41/89] =?UTF-8?q?refactor(TokenProvider)=20:=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/auth/service/TokenProvider.java | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/aliens/backend/auth/service/TokenProvider.java b/src/main/java/com/aliens/backend/auth/service/TokenProvider.java index 73341bf5..0fc38ea0 100644 --- a/src/main/java/com/aliens/backend/auth/service/TokenProvider.java +++ b/src/main/java/com/aliens/backend/auth/service/TokenProvider.java @@ -2,6 +2,8 @@ import com.aliens.backend.auth.controller.dto.LoginMember; import com.aliens.backend.auth.domain.MemberRole; +import com.aliens.backend.global.error.TokenError; +import com.aliens.backend.global.exception.RestApiException; import com.aliens.backend.global.property.JWTProperties; import io.jsonwebtoken.Claims; import io.jsonwebtoken.ExpiredJwtException; @@ -61,13 +63,18 @@ private Claims getClaimsFrom(final LoginMember loginMember, final Long tokenId) } public LoginMember getLoginMemberFromToken(final String token) { - Claims claims = Jwts.parserBuilder() - .setSigningKey(Keys.hmacShaKeyFor(jwtProperties.getBytesSecretKey())) - .build() - .parseClaimsJws(removePrefix(token)) - .getBody(); - - return new LoginMember(Long.parseLong(String.valueOf(claims.get("memberId"))), MemberRole.of((Integer)claims.get("role"))); + try { + Claims claims = Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(jwtProperties.getBytesSecretKey())) + .build() + .parseClaimsJws(removePrefix(token)) + .getBody(); + return new LoginMember(Long.parseLong(String.valueOf(claims.get("memberId"))), MemberRole.of((Integer)claims.get("role"))); + } catch (ExpiredJwtException e) { + throw new RestApiException(TokenError.EXPIRED_ACCESS_TOKEN); + } catch (Exception e) { + throw new RestApiException(TokenError.INVALID_TOKEN); + } } private String removePrefix(String token) { @@ -90,12 +97,18 @@ public boolean isNotExpiredToken(final String token) { } public Long getTokenIdFromToken(final String refreshToken) { - Claims claims = Jwts.parserBuilder() - .setSigningKey(Keys.hmacShaKeyFor(jwtProperties.getBytesSecretKey())) - .build() - .parseClaimsJws(removePrefix(refreshToken)) - .getBody(); - - return Long.parseLong(String.valueOf(claims.get("tokenId"))); + try { + Claims claims = Jwts.parserBuilder() + .setSigningKey(Keys.hmacShaKeyFor(jwtProperties.getBytesSecretKey())) + .build() + .parseClaimsJws(removePrefix(refreshToken)) + .getBody(); + + return Long.parseLong(String.valueOf(claims.get("tokenId"))); + } catch (ExpiredJwtException e) { + throw new RestApiException(TokenError.EXPIRED_ACCESS_TOKEN); + } catch (Exception e) { + throw new RestApiException(TokenError.INVALID_TOKEN); + } } } From 4a11276e159f0a60780f047007da837bb7e007b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 14:50:05 +0900 Subject: [PATCH 42/89] =?UTF-8?q?refactor(JWTProperties)=20:=20=ED=95=84?= =?UTF-8?q?=EC=9A=94=EC=97=86=EB=8A=94=20ChatTokenValidTime=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=B0=8F=20RefreshTokenTime=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/property/JWTProperties.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/aliens/backend/global/property/JWTProperties.java b/src/main/java/com/aliens/backend/global/property/JWTProperties.java index aa98dad7..85d2248a 100644 --- a/src/main/java/com/aliens/backend/global/property/JWTProperties.java +++ b/src/main/java/com/aliens/backend/global/property/JWTProperties.java @@ -11,13 +11,10 @@ public class JWTProperties { private String secretKey; @Value("${jwt.access_token_valid_time}") - private long accessTokenValidTime; + private Long accessTokenValidTime; @Value("${jwt.refresh_token_valid_time}") - private long refreshTokenValidTime ; - - @Value("${jwt.chat_token_valid_time}") - private long chatTokenValidTime ; + private Long refreshTokenValidTime ; public byte[] getBytesSecretKey() { @@ -32,8 +29,12 @@ public long getAccessTokenValidTime() { return accessTokenValidTime; } - public void setAccessTokenValidTime(Long time) { + public void setAccessTokenValidTime(final Long time) { accessTokenValidTime = time; } + + public void setRefreshTokenValidTime(final Long time) { + refreshTokenValidTime = time; + } } From 089e4d1e5fe653044438762e35b08404049b95fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 14:51:59 +0900 Subject: [PATCH 43/89] =?UTF-8?q?test(TokenProviderTest)=20:=20=EC=8B=A4?= =?UTF-8?q?=ED=8C=A8=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/service/TokenProviderTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java b/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java index 2dc807e5..f99735e6 100644 --- a/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java +++ b/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java @@ -2,6 +2,7 @@ import com.aliens.backend.auth.controller.dto.LoginMember; import com.aliens.backend.auth.domain.MemberRole; +import com.aliens.backend.global.exception.RestApiException; import com.aliens.backend.global.property.JWTProperties; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -62,6 +63,16 @@ void getLoginMemberFromAccessTokenTest() { Assertions.assertEquals(givenLoginMember,result); } + @Test + @DisplayName("토큰으로부터 로그인멤버 정보 추출 실패 - 올바르지 않은 토큰") + void getLoginMemberFromInvalidTokenFailTest() { + //Given + String InValidAccessToken = "올바르지 않은 토크"; + + //When & Then + Assertions.assertThrows(RestApiException.class, () -> tokenProvider.getLoginMemberFromToken(InValidAccessToken)); + } + @Test @DisplayName("만료된 토큰 검증") void expiredTokenTest() { @@ -102,4 +113,26 @@ void getTokenIdFromTokenTest() { //Then Assertions.assertEquals(result, givenTokenId); } + + @Test + @DisplayName("리프레시토큰으로부터 토큰 아이디 추출 실패 - 올바르지 않은 토큰") + void getTokenIdFromInvalidTokenFailTest() { + //Given + String InvalidRefreshToken = "올바르지 않은 토큰"; + + //When & Then + Assertions.assertThrows(RestApiException.class, () -> tokenProvider.getTokenIdFromToken(InvalidRefreshToken)); + } + + @Test + @DisplayName("리프레시토큰으로부터 토큰 아이디 추출 실패 - 만료된 토큰") + void getTokenIdFromExpiredTokenFailTest() { + //Given + jwtProperties.setRefreshTokenValidTime(1L); //RefreshToken 유효기한 짧게변경 + String expiredRefreshToken = tokenProvider.generateRefreshToken(givenLoginMember, givenTokenId); + jwtProperties.setRefreshTokenValidTime(2592000000L); //RefreshToken 유효기한 원상복구 + + //When & Then + Assertions.assertThrows(RestApiException.class, () -> tokenProvider.getTokenIdFromToken(expiredRefreshToken)); + } } From 1fc79e0afaac02b4c001a11894477b122f8df0f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 14:59:55 +0900 Subject: [PATCH 44/89] =?UTF-8?q?remove(BaseException,=20ErrorResponse,=20?= =?UTF-8?q?TokenException,=20MemberException=20)=20:=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/exception/BaseException.java | 20 ----------- .../global/exception/ErrorResponse.java | 34 ------------------- .../global/exception/MemberException.java | 20 ----------- .../global/exception/TokenException.java | 20 ----------- 4 files changed, 94 deletions(-) delete mode 100644 src/main/java/com/aliens/backend/global/exception/BaseException.java delete mode 100644 src/main/java/com/aliens/backend/global/exception/ErrorResponse.java delete mode 100644 src/main/java/com/aliens/backend/global/exception/MemberException.java delete mode 100644 src/main/java/com/aliens/backend/global/exception/TokenException.java diff --git a/src/main/java/com/aliens/backend/global/exception/BaseException.java b/src/main/java/com/aliens/backend/global/exception/BaseException.java deleted file mode 100644 index d60ecbdd..00000000 --- a/src/main/java/com/aliens/backend/global/exception/BaseException.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.aliens.backend.global.exception; - -import com.aliens.backend.global.error.ErrorCode; - -public class BaseException extends RuntimeException { - private final ErrorResponse errorResponse; - - public BaseException(final ErrorCode code) { - this.errorResponse = ErrorResponse.from(code); - } - - public ErrorResponse getErrorResponse() { - return errorResponse; - } - - @Override - public String getMessage() { - return errorResponse.getMessage(); - } -} \ No newline at end of file diff --git a/src/main/java/com/aliens/backend/global/exception/ErrorResponse.java b/src/main/java/com/aliens/backend/global/exception/ErrorResponse.java deleted file mode 100644 index 2cabc7ce..00000000 --- a/src/main/java/com/aliens/backend/global/exception/ErrorResponse.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.aliens.backend.global.exception; - -import com.aliens.backend.global.error.ErrorCode; - -public class ErrorResponse { - - private final String code; - private final String message; - - private ErrorResponse(final String code, final String message) { - this.code = code; - this.message = message; - } - - static ErrorResponse from(final ErrorCode errorCode) { - return new ErrorResponse(errorCode.getCode(), errorCode.getMessage()); - } - - public String getCode() { - return code; - } - - public String getMessage() { - return message; - } - - @Override - public String toString() { - return "ErrorResponse{" + - "code='" + code + '\'' + - ", message='" + message + '\'' + - '}'; - } -} diff --git a/src/main/java/com/aliens/backend/global/exception/MemberException.java b/src/main/java/com/aliens/backend/global/exception/MemberException.java deleted file mode 100644 index a8ee01dd..00000000 --- a/src/main/java/com/aliens/backend/global/exception/MemberException.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.aliens.backend.global.exception; - -import com.aliens.backend.global.error.ErrorCode; - -public class MemberException extends RuntimeException{ - private final ErrorResponse errorResponse; - - public MemberException(final ErrorCode code) { - this.errorResponse = ErrorResponse.from(code); - } - - public ErrorResponse getErrorResponse() { - return errorResponse; - } - - @Override - public String getMessage() { - return errorResponse.getMessage(); - } -} \ No newline at end of file diff --git a/src/main/java/com/aliens/backend/global/exception/TokenException.java b/src/main/java/com/aliens/backend/global/exception/TokenException.java deleted file mode 100644 index 3bce43b4..00000000 --- a/src/main/java/com/aliens/backend/global/exception/TokenException.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.aliens.backend.global.exception; - -import com.aliens.backend.global.error.ErrorCode; - -public class TokenException extends RuntimeException{ - private final ErrorResponse errorResponse; - - public TokenException(final ErrorCode code) { - this.errorResponse = ErrorResponse.from(code); - } - - public ErrorResponse getErrorResponse() { - return errorResponse; - } - - @Override - public String getMessage() { - return errorResponse.getMessage(); - } -} \ No newline at end of file From caeea999d981dc63eb9791c42dd4f7a252b32a8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 15:00:28 +0900 Subject: [PATCH 45/89] =?UTF-8?q?refactor(ErrorCode)=20:=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=EC=BD=94=EB=93=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EC=A0=95=EC=9D=98=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/aliens/backend/global/error/ErrorCode.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/aliens/backend/global/error/ErrorCode.java b/src/main/java/com/aliens/backend/global/error/ErrorCode.java index 0e73ed63..6e658fe9 100644 --- a/src/main/java/com/aliens/backend/global/error/ErrorCode.java +++ b/src/main/java/com/aliens/backend/global/error/ErrorCode.java @@ -1,6 +1,10 @@ package com.aliens.backend.global.error; +import org.springframework.http.HttpStatus; + public interface ErrorCode { - String getCode(); + String getDevelopCode(); + HttpStatus getHttpStatus(); String getMessage(); } + From 9e9167592c08fa6c1e8bbbe32baf48486dd64933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 15:01:26 +0900 Subject: [PATCH 46/89] =?UTF-8?q?feat(CommonError)=20:=20=EC=84=9C?= =?UTF-8?q?=EB=B2=84=EC=97=90=EB=9F=AC=EB=A5=BC=20=EB=8B=B4=EB=8B=B9?= =?UTF-8?q?=ED=95=A0=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/error/CommonError.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/error/CommonError.java diff --git a/src/main/java/com/aliens/backend/global/error/CommonError.java b/src/main/java/com/aliens/backend/global/error/CommonError.java new file mode 100644 index 00000000..fd278ed6 --- /dev/null +++ b/src/main/java/com/aliens/backend/global/error/CommonError.java @@ -0,0 +1,33 @@ +package com.aliens.backend.global.error; + +import org.springframework.http.HttpStatus; + +public enum CommonError implements ErrorCode { + + INTERNAL_SERVER_ERROR(HttpStatus.NOT_FOUND, "S1", "에러"); + + private final HttpStatus httpStatusCode; + private final String developCode; + private final String message; + + CommonError(final HttpStatus httpStatusCode, final String code, final String message) { + this.httpStatusCode = httpStatusCode; + this.developCode = code; + this.message = message; + } + + @Override + public HttpStatus getHttpStatus() { + return httpStatusCode; + } + + @Override + public String getDevelopCode() { + return developCode; + } + + @Override + public String getMessage() { + return message; + } +} \ No newline at end of file From f78a9f07276dd2b2a71908d19615ace260b13bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 15:02:13 +0900 Subject: [PATCH 47/89] =?UTF-8?q?refactor(MemberError,=20TokenError)=20:?= =?UTF-8?q?=20ErrorCode=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20?= =?UTF-8?q?=EB=82=B4=EB=B6=80=20=EA=B5=AC=ED=98=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/error/MemberError.java | 31 +++++++++++++------ .../backend/global/error/TokenError.java | 31 +++++++++++++------ 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/aliens/backend/global/error/MemberError.java b/src/main/java/com/aliens/backend/global/error/MemberError.java index 9ee5808e..6999d5f6 100644 --- a/src/main/java/com/aliens/backend/global/error/MemberError.java +++ b/src/main/java/com/aliens/backend/global/error/MemberError.java @@ -1,26 +1,37 @@ package com.aliens.backend.global.error; -public enum MemberError implements ErrorCode{ +import org.springframework.http.HttpStatus; - INVALID_PASSWORD("M1", "올바르지 않은 형식의 비밀번호"), - INVALID_EMAIL("M2", "올바르지 않은 형식의 이메일"), - NULL_MEMBER("M3", "해당 Member 엔티티 조회 불가"), - INCORRECT_PASSWORD("M4", "비밀번호 틀림"), +public enum MemberError implements ErrorCode { + + INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "M1", "올바르지 않은 형식의 비밀번호"), + INVALID_EMAIL(HttpStatus.BAD_REQUEST, "M2", "올바르지 않은 형식의 이메일"), + NULL_MEMBER(HttpStatus.UNPROCESSABLE_ENTITY, "M3", "해당 Member 엔티티 조회 불가"), + INCORRECT_PASSWORD(HttpStatus.SERVICE_UNAVAILABLE, "M4", "비밀번호 틀림"), ; - private final String code; + private final HttpStatus httpStatusCode; + private final String developCode; private final String message; - MemberError(final String code, final String message) { - this.code = code; + MemberError(final HttpStatus httpStatusCode, final String code, final String message) { + this.httpStatusCode = httpStatusCode; + this.developCode = code; this.message = message; } - public String getCode() { - return code; + @Override + public HttpStatus getHttpStatus() { + return httpStatusCode; } + @Override public String getMessage() { return message; } + + @Override + public String getDevelopCode() { + return developCode; + } } \ No newline at end of file diff --git a/src/main/java/com/aliens/backend/global/error/TokenError.java b/src/main/java/com/aliens/backend/global/error/TokenError.java index b615c8ed..243c2501 100644 --- a/src/main/java/com/aliens/backend/global/error/TokenError.java +++ b/src/main/java/com/aliens/backend/global/error/TokenError.java @@ -1,25 +1,36 @@ package com.aliens.backend.global.error; +import org.springframework.http.HttpStatus; + public enum TokenError implements ErrorCode { - INVALID_TOKEN("T1", "올바르지 않은 AccessToken"), - EXPIRED_ACCESS_TOKEN("T2", "만료된 AccessToken"), - EXPIRED_REFRESH_TOKEN("T3", "만료된 ReFreshToken"), - NULL_REFRESH_TOKEN("T4", "존재하지 않은 ReFreshToken 접근"), - NOT_ACCESS_TOKEN_FOR_REISSUE("T5","재발급하기에는 유효기간이 남은 AccessToken"); + INVALID_TOKEN(HttpStatus.BAD_REQUEST,"T1", "올바르지 않은 AccessToken"), + EXPIRED_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED,"T2", "만료된 AccessToken"), + EXPIRED_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, "T3", "만료된 ReFreshToken"), + NULL_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED,"T4", "존재하지 않은 ReFreshToken 접근"), + NOT_ACCESS_TOKEN_FOR_REISSUE(HttpStatus.BAD_REQUEST,"T5","재발급하기에는 유효기간이 남은 AccessToken"); - private final String code; + private final HttpStatus httpStatusCode; + private final String developCode; private final String message; - TokenError(final String code, final String message) { - this.code = code; + TokenError(final HttpStatus httpStatusCode, final String code, final String message) { + this.httpStatusCode = httpStatusCode; + this.developCode = code; this.message = message; } - public String getCode() { - return code; + @Override + public HttpStatus getHttpStatus() { + return httpStatusCode; + } + + @Override + public String getDevelopCode() { + return developCode; } + @Override public String getMessage() { return message; } From fb5acf408e8285663280b0666b824dff8298a78e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 15:02:59 +0900 Subject: [PATCH 48/89] =?UTF-8?q?refactor(RestApiException)=20:=20Controll?= =?UTF-8?q?er=20=EC=97=90=EC=84=9C=20=EB=B1=89=EC=9D=84=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=EB=A5=BC=20=EA=B0=80=EC=A7=84=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/exception/RestApiException.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/exception/RestApiException.java diff --git a/src/main/java/com/aliens/backend/global/exception/RestApiException.java b/src/main/java/com/aliens/backend/global/exception/RestApiException.java new file mode 100644 index 00000000..74b9deb3 --- /dev/null +++ b/src/main/java/com/aliens/backend/global/exception/RestApiException.java @@ -0,0 +1,16 @@ +package com.aliens.backend.global.exception; + +import com.aliens.backend.global.error.ErrorCode; + +public class RestApiException extends RuntimeException { + + private final ErrorCode errorCode; + + public RestApiException(final ErrorCode errorCode) { + this.errorCode = errorCode; + } + + public ErrorCode getErrorCode() { + return errorCode; + } +} From 0fb715bae7e3c9837bf2a0135f2bf1ee92a6af63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 15:03:46 +0900 Subject: [PATCH 49/89] =?UTF-8?q?refactor(PasswordEncoder,=20AuthService)?= =?UTF-8?q?=20:=20=EC=98=88=EC=99=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20imp?= =?UTF-8?q?ort=EB=AC=B8=20=EC=88=98=EC=A0=95=EA=B3=BC=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aliens/backend/auth/service/AuthService.java | 15 +++++++-------- .../backend/auth/service/PasswordEncoder.java | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/aliens/backend/auth/service/AuthService.java b/src/main/java/com/aliens/backend/auth/service/AuthService.java index e1646817..137f00b7 100644 --- a/src/main/java/com/aliens/backend/auth/service/AuthService.java +++ b/src/main/java/com/aliens/backend/auth/service/AuthService.java @@ -8,8 +8,7 @@ import com.aliens.backend.auth.domain.repository.TokenRepository; import com.aliens.backend.global.error.MemberError; import com.aliens.backend.global.error.TokenError; -import com.aliens.backend.global.exception.MemberException; -import com.aliens.backend.global.exception.TokenException; +import com.aliens.backend.global.exception.RestApiException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -41,12 +40,12 @@ public AuthToken login(LoginRequest loginRequest) { } private Member getMemberEntityFromEmail(final String email) { - return memberRepository.findByEmail(email).orElseThrow(() -> new MemberException(MemberError.NULL_MEMBER)); + return memberRepository.findByEmail(email).orElseThrow(() -> new RestApiException(MemberError.NULL_MEMBER)); } private void passwordCheck(final String password, final Member member) { if(!member.isCorrectPassword(passwordEncoder.encrypt(password))) { - throw new MemberException(MemberError.INCORRECT_PASSWORD); + throw new RestApiException(MemberError.INCORRECT_PASSWORD); } } @@ -77,7 +76,7 @@ public String logout(final AuthToken authToken) { } private Token getTokenEntity(final Long tokenId) { - return tokenRepository.findById(tokenId).orElseThrow(() -> new TokenException(TokenError.NULL_REFRESH_TOKEN)); + return tokenRepository.findById(tokenId).orElseThrow(() -> new RestApiException(TokenError.NULL_REFRESH_TOKEN)); } @Transactional @@ -90,10 +89,10 @@ public AuthToken reissue(final AuthToken authToken) { private void tokenExpiredCheck(final AuthToken authToken) { if (tokenProvider.isNotExpiredToken(authToken.accessToken())) { - throw new TokenException(TokenError.NOT_ACCESS_TOKEN_FOR_REISSUE); + throw new RestApiException(TokenError.NOT_ACCESS_TOKEN_FOR_REISSUE); } if (!tokenProvider.isNotExpiredToken(authToken.refreshToken())) { - throw new TokenException(TokenError.EXPIRED_REFRESH_TOKEN); + throw new RestApiException(TokenError.EXPIRED_REFRESH_TOKEN); } dbTokenExpiredCheck(authToken.refreshToken()); @@ -104,7 +103,7 @@ private void dbTokenExpiredCheck(final String refreshToken) { Token token = getTokenEntity(tokenId); if (token.isExpired()) { - throw new TokenException(TokenError.EXPIRED_REFRESH_TOKEN); + throw new RestApiException(TokenError.EXPIRED_REFRESH_TOKEN); } token.changeRecentLogin(); } diff --git a/src/main/java/com/aliens/backend/auth/service/PasswordEncoder.java b/src/main/java/com/aliens/backend/auth/service/PasswordEncoder.java index 8bd82e17..2d829498 100644 --- a/src/main/java/com/aliens/backend/auth/service/PasswordEncoder.java +++ b/src/main/java/com/aliens/backend/auth/service/PasswordEncoder.java @@ -1,7 +1,7 @@ package com.aliens.backend.auth.service; import com.aliens.backend.global.error.MemberError; -import com.aliens.backend.global.exception.MemberException; +import com.aliens.backend.global.exception.RestApiException; import com.aliens.backend.global.property.EncodeProperties; import org.springframework.stereotype.Component; @@ -30,7 +30,7 @@ public String encrypt(String input) { return Base64.getEncoder().encodeToString(hash); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { - throw new MemberException(MemberError.INVALID_PASSWORD); + throw new RestApiException(MemberError.INVALID_PASSWORD); } } } From 1f0fe1a650752a54c28e47b1ec86bf90981c67f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 15:04:42 +0900 Subject: [PATCH 50/89] =?UTF-8?q?feat(ApiExceptionHandler,=20GlobalExcepti?= =?UTF-8?q?onHandler)=20:=20Spring=EC=97=90=EC=84=9C=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=EB=A5=BC=20=EB=B0=9B=EC=95=84=20ResponseEntity=EB=A1=9C=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=B4=EC=A3=BC=EB=8A=94=20=ED=95=B8?= =?UTF-8?q?=EB=93=A4=EB=9F=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/ApiExceptionHandler.java | 29 +++++++++++++++++++ .../global/config/GlobalExceptionHandler.java | 29 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/config/ApiExceptionHandler.java create mode 100644 src/main/java/com/aliens/backend/global/config/GlobalExceptionHandler.java diff --git a/src/main/java/com/aliens/backend/global/config/ApiExceptionHandler.java b/src/main/java/com/aliens/backend/global/config/ApiExceptionHandler.java new file mode 100644 index 00000000..89e0f7e2 --- /dev/null +++ b/src/main/java/com/aliens/backend/global/config/ApiExceptionHandler.java @@ -0,0 +1,29 @@ +package com.aliens.backend.global.config; + +import com.aliens.backend.global.error.ErrorCode; +import com.aliens.backend.global.exception.RestApiException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.Order; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +@Order(value = Integer.MIN_VALUE) +public class ApiExceptionHandler { + + private final Logger logger = LoggerFactory.getLogger("RestApi 에러 로그"); + + @ExceptionHandler(value = RestApiException.class) + public ResponseEntity apiException(RestApiException apiException) { + ErrorCode errorCode = apiException.getErrorCode(); + logger.info(errorCode.getMessage()); + + return ResponseEntity + .status(errorCode.getHttpStatus()) + .body( + errorCode.getDevelopCode() + ); + } +} \ No newline at end of file diff --git a/src/main/java/com/aliens/backend/global/config/GlobalExceptionHandler.java b/src/main/java/com/aliens/backend/global/config/GlobalExceptionHandler.java new file mode 100644 index 00000000..d5b733f4 --- /dev/null +++ b/src/main/java/com/aliens/backend/global/config/GlobalExceptionHandler.java @@ -0,0 +1,29 @@ +package com.aliens.backend.global.config; + +import com.aliens.backend.global.error.CommonError; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.Order; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import static java.lang.Integer.MAX_VALUE; + +@RestControllerAdvice +@Order(value = MAX_VALUE) +public class GlobalExceptionHandler { + + private final Logger logger = LoggerFactory.getLogger("Global 에러 로그"); + + @ExceptionHandler(value = Exception.class) + public ResponseEntity exception (Exception exception) { + logger.info(exception.getMessage()); + + return ResponseEntity + .status(500) + .body( + CommonError.INTERNAL_SERVER_ERROR.getDevelopCode() + ); + } +} \ No newline at end of file From c91be5694a71f4a1617241ff34b78861f476f410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 15:05:07 +0900 Subject: [PATCH 51/89] =?UTF-8?q?feat(Login)=20:=20Resolver=EA=B0=80=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=EB=90=A0=20=EC=95=A0=EB=85=B8=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/aliens/backend/global/config/resolver/Login.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/config/resolver/Login.java diff --git a/src/main/java/com/aliens/backend/global/config/resolver/Login.java b/src/main/java/com/aliens/backend/global/config/resolver/Login.java new file mode 100644 index 00000000..aebb6162 --- /dev/null +++ b/src/main/java/com/aliens/backend/global/config/resolver/Login.java @@ -0,0 +1,9 @@ +package com.aliens.backend.global.config.resolver; + +import java.lang.annotation.*; + +@Target({ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Login { +} \ No newline at end of file From c73c3156095c54f4747c7e4183bf984776f37182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 15:05:52 +0900 Subject: [PATCH 52/89] =?UTF-8?q?feat(TokenInfoResolver)=20:=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=EC=9D=84=20=EB=94=94=EC=BD=94=EB=94=A9=ED=95=98?= =?UTF-8?q?=EC=97=AC=20LoginMember=EB=A1=9C=20=EB=B0=94=EC=9D=B8=EB=94=A9?= =?UTF-8?q?=ED=95=B4=EC=A3=BC=EB=8A=94=20=EB=A6=AC=EC=A1=B8=EB=B2=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/resolver/TokenInfoResolver.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/config/resolver/TokenInfoResolver.java diff --git a/src/main/java/com/aliens/backend/global/config/resolver/TokenInfoResolver.java b/src/main/java/com/aliens/backend/global/config/resolver/TokenInfoResolver.java new file mode 100644 index 00000000..f0aea721 --- /dev/null +++ b/src/main/java/com/aliens/backend/global/config/resolver/TokenInfoResolver.java @@ -0,0 +1,33 @@ +package com.aliens.backend.global.config.resolver; + +import com.aliens.backend.auth.controller.dto.LoginMember; +import com.aliens.backend.auth.service.TokenProvider; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +@Component +public class TokenInfoResolver implements HandlerMethodArgumentResolver { + + private final TokenProvider tokenProvider; + + public TokenInfoResolver(final TokenProvider tokenProvider) { + this.tokenProvider = tokenProvider; + } + + @Override + public boolean supportsParameter(final MethodParameter parameter) { + return parameter.getParameterType().isAssignableFrom(LoginMember.class) + && parameter.hasParameterAnnotation(Login.class); + } + + @Override + public Object resolveArgument(final MethodParameter parameter,final ModelAndViewContainer mavContainer, + final NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { + String accessToken = webRequest.getHeader("Authorization"); + return tokenProvider.getLoginMemberFromToken(accessToken); + } +} \ No newline at end of file From 75b3ba0058503f2d16d79591b76efcc180ef4f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 15:06:27 +0900 Subject: [PATCH 53/89] =?UTF-8?q?feat(WebConfig)=20:=20=ED=86=A0=ED=81=B0?= =?UTF-8?q?=EC=9D=84=20=EB=94=94=EC=BD=94=EB=94=A9=ED=95=98=EC=97=AC=20Log?= =?UTF-8?q?inMember=EB=A1=9C=20=EB=B0=94=EC=9D=B8=EB=94=A9=ED=95=B4?= =?UTF-8?q?=EC=A3=BC=EB=8A=94=20=EB=A6=AC=EC=A1=B8=EB=B2=84=EB=A5=BC=20web?= =?UTF-8?q?Config=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/config/WebConfig.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/config/WebConfig.java diff --git a/src/main/java/com/aliens/backend/global/config/WebConfig.java b/src/main/java/com/aliens/backend/global/config/WebConfig.java new file mode 100644 index 00000000..4082e80b --- /dev/null +++ b/src/main/java/com/aliens/backend/global/config/WebConfig.java @@ -0,0 +1,23 @@ +package com.aliens.backend.global.config; + +import com.aliens.backend.global.config.resolver.TokenInfoResolver; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + private final TokenInfoResolver tokenInfoResolver; + + public WebConfig(final TokenInfoResolver tokenInfoResolver) { + this.tokenInfoResolver = tokenInfoResolver; + } + + @Override + public void addArgumentResolvers(List argumentResolvers) { + argumentResolvers.add(tokenInfoResolver); + } +} From 75f655eeb1f9a0c1eadb05b2d327080843ad459a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sat, 20 Jan 2024 18:29:00 +0900 Subject: [PATCH 54/89] =?UTF-8?q?refactor(ApiExceptionHandler,=20GlobalExc?= =?UTF-8?q?eptionHandler)=20:=20exception=20=EB=94=94=EB=A0=89=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/{config => exception}/ApiExceptionHandler.java | 3 +-- .../global/{config => exception}/GlobalExceptionHandler.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) rename src/main/java/com/aliens/backend/global/{config => exception}/ApiExceptionHandler.java (90%) rename src/main/java/com/aliens/backend/global/{config => exception}/GlobalExceptionHandler.java (95%) diff --git a/src/main/java/com/aliens/backend/global/config/ApiExceptionHandler.java b/src/main/java/com/aliens/backend/global/exception/ApiExceptionHandler.java similarity index 90% rename from src/main/java/com/aliens/backend/global/config/ApiExceptionHandler.java rename to src/main/java/com/aliens/backend/global/exception/ApiExceptionHandler.java index 89e0f7e2..738d79e4 100644 --- a/src/main/java/com/aliens/backend/global/config/ApiExceptionHandler.java +++ b/src/main/java/com/aliens/backend/global/exception/ApiExceptionHandler.java @@ -1,7 +1,6 @@ -package com.aliens.backend.global.config; +package com.aliens.backend.global.exception; import com.aliens.backend.global.error.ErrorCode; -import com.aliens.backend.global.exception.RestApiException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; diff --git a/src/main/java/com/aliens/backend/global/config/GlobalExceptionHandler.java b/src/main/java/com/aliens/backend/global/exception/GlobalExceptionHandler.java similarity index 95% rename from src/main/java/com/aliens/backend/global/config/GlobalExceptionHandler.java rename to src/main/java/com/aliens/backend/global/exception/GlobalExceptionHandler.java index d5b733f4..a764c412 100644 --- a/src/main/java/com/aliens/backend/global/config/GlobalExceptionHandler.java +++ b/src/main/java/com/aliens/backend/global/exception/GlobalExceptionHandler.java @@ -1,4 +1,4 @@ -package com.aliens.backend.global.config; +package com.aliens.backend.global.exception; import com.aliens.backend.global.error.CommonError; import org.slf4j.Logger; From ab9848083afb7b678f365956029f2574efd5775a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sun, 21 Jan 2024 22:20:48 +0900 Subject: [PATCH 55/89] =?UTF-8?q?chore=20(build.gradle)=20:=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EC=97=85=EB=A1=9C=EB=93=9C=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20dependencies=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build.gradle b/build.gradle index 7d6c47c2..22f7af56 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,11 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' asciidoctor 'org.springframework.restdocs:spring-restdocs-asciidoctor' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' + + // 파일 업로드 + implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + testImplementation 'io.findify:s3mock_2.13:0.2.6' + } ext { From 1f7c7a0929bffa4ce1a9d86e85b0ca1b8f1d32ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sun, 21 Jan 2024 22:21:40 +0900 Subject: [PATCH 56/89] =?UTF-8?q?feat(AWSConfig)=20:=20AmazonS3Client=20?= =?UTF-8?q?=EB=A5=BC=20=EC=96=BB=EA=B8=B0=EC=9C=84=ED=95=9C=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aliens/backend/uploader/AWSConfig.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/java/com/aliens/backend/uploader/AWSConfig.java diff --git a/src/main/java/com/aliens/backend/uploader/AWSConfig.java b/src/main/java/com/aliens/backend/uploader/AWSConfig.java new file mode 100644 index 00000000..1a2d5a6c --- /dev/null +++ b/src/main/java/com/aliens/backend/uploader/AWSConfig.java @@ -0,0 +1,27 @@ +package com.aliens.backend.uploader; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AWSConfig { + + private final S3UploadProperties s3UploadProperties; + + public AWSConfig(final S3UploadProperties s3UploadProperties) { + this.s3UploadProperties = s3UploadProperties; + } + + @Bean + public AmazonS3Client amazonS3Client() { + BasicAWSCredentials basicAWSCredentials = new BasicAWSCredentials(s3UploadProperties.getAccessKey(), s3UploadProperties.getSecretKey()); + return (AmazonS3Client) AmazonS3ClientBuilder.standard() + .withRegion(s3UploadProperties.getRegion()) + .withCredentials(new AWSStaticCredentialsProvider(basicAWSCredentials)) + .build(); + } +} \ No newline at end of file From d2c721931ec45d896809eddad499ff5c0034f9f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sun, 21 Jan 2024 22:22:20 +0900 Subject: [PATCH 57/89] =?UTF-8?q?feat(LocalUploadProperties,=20S3UploadPro?= =?UTF-8?q?perties)=20:=20=ED=8C=8C=EC=9D=BC=20=EC=97=85=EB=A1=9C=EB=93=9C?= =?UTF-8?q?=EB=A5=BC=20=EC=9C=84=ED=95=9C=20Properties=20=EA=B0=92=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../uploader/LocalUploadProperties.java | 24 +++++++++++++ .../backend/uploader/S3UploadProperties.java | 36 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/main/java/com/aliens/backend/uploader/LocalUploadProperties.java create mode 100644 src/main/java/com/aliens/backend/uploader/S3UploadProperties.java diff --git a/src/main/java/com/aliens/backend/uploader/LocalUploadProperties.java b/src/main/java/com/aliens/backend/uploader/LocalUploadProperties.java new file mode 100644 index 00000000..645a0481 --- /dev/null +++ b/src/main/java/com/aliens/backend/uploader/LocalUploadProperties.java @@ -0,0 +1,24 @@ +package com.aliens.backend.uploader; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class LocalUploadProperties { + + @Value("${spring.servlet.multipart.location}") + private String uploadPath; + + @Value("${spring.servlet.multipart.enabled}") + private boolean available; + + @Value("${spring.servlet.multipart.max-request-size}") + private String maxRequestSize; + + @Value("${spring.servlet.multipart.max-file-size}") + private String maxFileSize; + + public String getUploadPath() { + return uploadPath; + } +} diff --git a/src/main/java/com/aliens/backend/uploader/S3UploadProperties.java b/src/main/java/com/aliens/backend/uploader/S3UploadProperties.java new file mode 100644 index 00000000..1329fd30 --- /dev/null +++ b/src/main/java/com/aliens/backend/uploader/S3UploadProperties.java @@ -0,0 +1,36 @@ +package com.aliens.backend.uploader; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class S3UploadProperties { + + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + @Value("${cloud.aws.credentials.access-key}") + private String accessKey; + + @Value("${cloud.aws.credentials.secret-key}") + private String secretKey; + + @Value("${cloud.aws.region.static}") + private String region; + + public String getBucket() { + return bucket; + } + + public String getAccessKey() { + return accessKey; + } + + public String getSecretKey() { + return secretKey; + } + + public String getRegion() { + return region; + } +} \ No newline at end of file From e1829c738cfc7de5b991d97d605efb2db9da73d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sun, 21 Jan 2024 22:22:37 +0900 Subject: [PATCH 58/89] =?UTF-8?q?feat(UploadFileRequest)=20:=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=97=85=EB=A1=9C=EB=93=9C=EB=A5=BC=20=EC=9C=84?= =?UTF-8?q?=ED=95=9C=20Dto=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/aliens/backend/uploader/UploadFileRequest.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/com/aliens/backend/uploader/UploadFileRequest.java diff --git a/src/main/java/com/aliens/backend/uploader/UploadFileRequest.java b/src/main/java/com/aliens/backend/uploader/UploadFileRequest.java new file mode 100644 index 00000000..cc449d7e --- /dev/null +++ b/src/main/java/com/aliens/backend/uploader/UploadFileRequest.java @@ -0,0 +1,8 @@ +package com.aliens.backend.uploader; + +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +public record UploadFileRequest(List files) { +} \ No newline at end of file From f52997edf371c5be9f498fa4452c92cc7949fc31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sun, 21 Jan 2024 22:23:21 +0900 Subject: [PATCH 59/89] =?UTF-8?q?feat(AwsS3Uploader)=20:=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EB=B0=8F=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A5=BC=20=EA=B0=80?= =?UTF-8?q?=EC=A7=84=20=EC=97=85=EB=A1=9C=EB=93=9C=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/uploader/AwsS3Uploader.java | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/main/java/com/aliens/backend/uploader/AwsS3Uploader.java diff --git a/src/main/java/com/aliens/backend/uploader/AwsS3Uploader.java b/src/main/java/com/aliens/backend/uploader/AwsS3Uploader.java new file mode 100644 index 00000000..fe067b79 --- /dev/null +++ b/src/main/java/com/aliens/backend/uploader/AwsS3Uploader.java @@ -0,0 +1,72 @@ +package com.aliens.backend.uploader; + +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.PutObjectRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Optional; +import java.util.UUID; + +@Component +public class AwsS3Uploader { + + private final AmazonS3Client amazonS3Client; + private final S3UploadProperties s3UploadProperties; + private final LocalUploadProperties localUploadProperties; + + public AwsS3Uploader(final AmazonS3Client amazonS3Client, final S3UploadProperties s3UploadProperties, final LocalUploadProperties localUploadProperties) { + this.amazonS3Client = amazonS3Client; + this.s3UploadProperties = s3UploadProperties; + this.localUploadProperties = localUploadProperties; + } + + public String upload(MultipartFile multipartFile) throws IOException { + File uploadFile = convert(multipartFile) + .orElseThrow(() -> new IllegalArgumentException("MultipartFile -> File convert fail")); + + return uploadToS3(uploadFile); + } + + //로컬에 파일생성 + private Optional convert(MultipartFile file) throws IOException { + File convertFile = new File(file.getOriginalFilename()); + if (convertFile.createNewFile()) { + try (FileOutputStream fos = new FileOutputStream(convertFile)) { + fos.write(file.getBytes()); + } + return Optional.of(convertFile); + } + + return Optional.empty(); + } + + private String uploadToS3(File uploadFile) { + String fileName = new StringBuilder() + .append(localUploadProperties.getUploadPath()).append("/") + .append(UUID.randomUUID()) + .append(uploadFile.getName()).toString(); + + String uploadImageUrl = putS3(uploadFile, fileName); // S3로 업로드 + uploadFile.delete(); // 로컬 파일 삭제 + return uploadImageUrl; + } + + // S3에 업로드 + private String putS3(File uploadFile, String fileName) { + amazonS3Client.putObject(new PutObjectRequest(s3UploadProperties.getBucket(), + fileName, uploadFile) + .withCannedAcl(CannedAccessControlList.PublicRead)); + return amazonS3Client.getUrl(s3UploadProperties.getBucket(),fileName).toString(); + } + + public boolean delete(String fileName) { + amazonS3Client.deleteObject(s3UploadProperties.getBucket(), fileName); + return true; + } +} \ No newline at end of file From d5143f6b10e2cc1745a314b7fd3055ac580207ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sun, 21 Jan 2024 22:24:37 +0900 Subject: [PATCH 60/89] =?UTF-8?q?test(AwsS3MockConfig)=20:=20=EC=99=B8?= =?UTF-8?q?=EB=B6=80=EB=A6=AC=EC=86=8C=EC=8A=A4=EC=9D=B8=20S3=EC=97=90=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=A0=80=EC=9E=A5=EC=9D=84=20Moc?= =?UTF-8?q?k=EC=9C=BC=EB=A1=9C=20=EC=A7=84=ED=96=89=ED=95=98=EA=B8=B0?= =?UTF-8?q?=EC=9C=84=ED=95=B4,=20S3Mock=20=EC=9D=84=20=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=ED=95=A9=EB=8B=88=EB=8B=A4.=20=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/uploader/AwsS3MockConfig.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/test/java/com/aliens/backend/uploader/AwsS3MockConfig.java diff --git a/src/test/java/com/aliens/backend/uploader/AwsS3MockConfig.java b/src/test/java/com/aliens/backend/uploader/AwsS3MockConfig.java new file mode 100644 index 00000000..53db67e0 --- /dev/null +++ b/src/test/java/com/aliens/backend/uploader/AwsS3MockConfig.java @@ -0,0 +1,40 @@ +package com.aliens.backend.uploader; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.AnonymousAWSCredentials; +import com.amazonaws.client.builder.AwsClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import io.findify.s3mock.S3Mock; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + +@TestConfiguration +public class AwsS3MockConfig { + + private final S3UploadProperties s3UploadProperties; + + public AwsS3MockConfig(S3UploadProperties s3UploadProperties) { + this.s3UploadProperties = s3UploadProperties; + } + + @Bean + public S3Mock s3Mock() { + return new S3Mock.Builder().withPort(8001).withInMemoryBackend().build(); + } + + @Bean + public AmazonS3 amazonS3(S3Mock s3Mock){ + s3Mock.start(); + AwsClientBuilder.EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration("http://localhost:8001", s3UploadProperties.getRegion()); + AmazonS3 client = AmazonS3ClientBuilder + .standard() + .withPathStyleAccessEnabled(true) + .withEndpointConfiguration(endpoint) + .withCredentials(new AWSStaticCredentialsProvider(new AnonymousAWSCredentials())) + .build(); + client.createBucket(s3UploadProperties.getBucket()); + + return client; + } +} From 415be8afe59d45285b0e4fffe232201d21e83f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sun, 21 Jan 2024 22:24:55 +0900 Subject: [PATCH 61/89] =?UTF-8?q?test(AwsS3UploaderTest)=20:=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=97=85=EB=A1=9C=EB=93=9C,=20=EC=82=AD=EC=A0=9C?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=EC=97=90=20=EB=8C=80=ED=95=9C=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/uploader/AwsS3UploaderTest.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/test/java/com/aliens/backend/uploader/AwsS3UploaderTest.java diff --git a/src/test/java/com/aliens/backend/uploader/AwsS3UploaderTest.java b/src/test/java/com/aliens/backend/uploader/AwsS3UploaderTest.java new file mode 100644 index 00000000..9d63685f --- /dev/null +++ b/src/test/java/com/aliens/backend/uploader/AwsS3UploaderTest.java @@ -0,0 +1,57 @@ +package com.aliens.backend.uploader; + +import io.findify.s3mock.S3Mock; +import org.assertj.core.api.Assertions; +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.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.mock.web.MockMultipartFile; + +import java.io.IOException; + +@Import(AwsS3MockConfig.class) +@SpringBootTest +class AwsS3UploaderTest { + + @Autowired + private S3Mock s3Mock; + + @Autowired + private AwsS3Uploader awsS3Uploader; + + @AfterEach + public void tearDown() { + s3Mock.stop(); + } + + @Test + @DisplayName("S3 파일 업로드 테스트") + void uploadTest() throws IOException { + // Given + String path = "test.png"; + String contentType = "image/png"; + MockMultipartFile file = new MockMultipartFile("test", path, contentType, "test".getBytes()); + + // When + String urlPath = awsS3Uploader.upload(file); + + // Then + Assertions.assertThat(urlPath).contains(path); + } + + @Test + @DisplayName("S3 파일 삭제 테스트") + void deleteTest() { + // Given + String path = "test.png"; + + // When + boolean result = awsS3Uploader.delete(path); + + // Then + org.junit.jupiter.api.Assertions.assertTrue(result); + } +} \ No newline at end of file From 79af37de92b45e73e34b64f254b52cafc751a3ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Sun, 21 Jan 2024 23:09:17 +0900 Subject: [PATCH 62/89] =?UTF-8?q?style(TokenProviderTest):=20=ED=86=A0?= =?UTF-8?q?=ED=81=AC=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/aliens/backend/auth/service/TokenProviderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java b/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java index f99735e6..07b94f05 100644 --- a/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java +++ b/src/test/java/com/aliens/backend/auth/service/TokenProviderTest.java @@ -67,7 +67,7 @@ void getLoginMemberFromAccessTokenTest() { @DisplayName("토큰으로부터 로그인멤버 정보 추출 실패 - 올바르지 않은 토큰") void getLoginMemberFromInvalidTokenFailTest() { //Given - String InValidAccessToken = "올바르지 않은 토크"; + String InValidAccessToken = "올바르지 않은 토큰"; //When & Then Assertions.assertThrows(RestApiException.class, () -> tokenProvider.getLoginMemberFromToken(InValidAccessToken)); From 5bbb27b9234048204413f1ac205cd1fe30097149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Tue, 23 Jan 2024 00:28:52 +0900 Subject: [PATCH 63/89] =?UTF-8?q?Revert=20"Revert=20"Feat/#14=20=EB=8C=80?= =?UTF-8?q?=EC=B9=AD=ED=82=A4=20=EC=95=94=ED=98=B8=ED=99=94,=20=EB=B3=B5?= =?UTF-8?q?=ED=98=B8=ED=99=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84""?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/encode/EncoderProperties.java | 27 ++++++++++++ .../backend/encode/SymmetricKeyEncoder.java | 32 +++++++++++++++ .../encode/SymmetricKeyEncoderTest.java | 41 +++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 src/main/java/com/aliens/backend/encode/EncoderProperties.java create mode 100644 src/main/java/com/aliens/backend/encode/SymmetricKeyEncoder.java create mode 100644 src/test/java/com/aliens/backend/encode/SymmetricKeyEncoderTest.java diff --git a/src/main/java/com/aliens/backend/encode/EncoderProperties.java b/src/main/java/com/aliens/backend/encode/EncoderProperties.java new file mode 100644 index 00000000..8d6e1a1e --- /dev/null +++ b/src/main/java/com/aliens/backend/encode/EncoderProperties.java @@ -0,0 +1,27 @@ +package com.aliens.backend.encode; + +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; + +@Component +public class EncoderProperties { + + @Value("${encode.symmetric.key}") + private String secretKey; + + private SecretKeySpec encodeKey; + + @PostConstruct + public void init() { + byte[] byteKey = secretKey.getBytes(StandardCharsets.UTF_8); + encodeKey = new SecretKeySpec(byteKey, "AES"); + } + + public SecretKeySpec getEncodeKey() { + return encodeKey; + } +} diff --git a/src/main/java/com/aliens/backend/encode/SymmetricKeyEncoder.java b/src/main/java/com/aliens/backend/encode/SymmetricKeyEncoder.java new file mode 100644 index 00000000..47e7b819 --- /dev/null +++ b/src/main/java/com/aliens/backend/encode/SymmetricKeyEncoder.java @@ -0,0 +1,32 @@ +package com.aliens.backend.encode; + +import org.springframework.stereotype.Component; + +import javax.crypto.Cipher; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +@Component +public class SymmetricKeyEncoder { + + private final EncoderProperties encoderProperties; + + public SymmetricKeyEncoder(final EncoderProperties encoderProperties) { + this.encoderProperties = encoderProperties; + } + + public String encrypt(String plaintext) throws Exception { + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.ENCRYPT_MODE, encoderProperties.getEncodeKey()); + byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(encryptedBytes); + } + + public String decrypt(String ciphertext) throws Exception { + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.DECRYPT_MODE, encoderProperties.getEncodeKey()); + byte[] decodedBytes = Base64.getDecoder().decode(ciphertext); + byte[] decryptedBytes = cipher.doFinal(decodedBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } +} diff --git a/src/test/java/com/aliens/backend/encode/SymmetricKeyEncoderTest.java b/src/test/java/com/aliens/backend/encode/SymmetricKeyEncoderTest.java new file mode 100644 index 00000000..3929f38c --- /dev/null +++ b/src/test/java/com/aliens/backend/encode/SymmetricKeyEncoderTest.java @@ -0,0 +1,41 @@ +package com.aliens.backend.encode; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SymmetricKeyEncoderTest { + + @Autowired + private SymmetricKeyEncoder symmetricKeyEncoder; + + @Test + @DisplayName("문자열 암호화 성공") + void encryptTest() throws Exception { + //Given + String givenInput = "testText"; + + //When + String result = symmetricKeyEncoder.encrypt(givenInput); + + //Then + Assertions.assertNotEquals(result, givenInput); + } + + @Test + @DisplayName("문자열 복호화 성공") + void decryptTest() throws Exception { + //Given + String givenInput = "testText"; + String code = symmetricKeyEncoder.encrypt(givenInput); + + //When + String result = symmetricKeyEncoder.decrypt(code); + + //Then + Assertions.assertEquals(result, givenInput); + } +} From 2ba264ae1ac5250380dcc85e11a02cbd036d433d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Tue, 23 Jan 2024 02:08:37 +0900 Subject: [PATCH 64/89] =?UTF-8?q?style(SymmetricKeyEncoder,=20PasswordEnco?= =?UTF-8?q?der)=20:=20=EC=95=94=ED=98=B8=ED=99=94=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/auth/service/PasswordEncoder.java | 10 +++++----- .../{ => global}/encode/SymmetricKeyEncoder.java | 13 +++++++------ ...roperties.java => PasswordEncodeProperties.java} | 2 +- .../property/SymmetricEncoderProperties.java} | 4 ++-- .../backend/encode/SymmetricKeyEncoderTest.java | 1 + 5 files changed, 16 insertions(+), 14 deletions(-) rename src/main/java/com/aliens/backend/{ => global}/encode/SymmetricKeyEncoder.java (60%) rename src/main/java/com/aliens/backend/global/property/{EncodeProperties.java => PasswordEncodeProperties.java} (90%) rename src/main/java/com/aliens/backend/{encode/EncoderProperties.java => global/property/SymmetricEncoderProperties.java} (87%) diff --git a/src/main/java/com/aliens/backend/auth/service/PasswordEncoder.java b/src/main/java/com/aliens/backend/auth/service/PasswordEncoder.java index 2d829498..6092023e 100644 --- a/src/main/java/com/aliens/backend/auth/service/PasswordEncoder.java +++ b/src/main/java/com/aliens/backend/auth/service/PasswordEncoder.java @@ -2,7 +2,7 @@ import com.aliens.backend.global.error.MemberError; import com.aliens.backend.global.exception.RestApiException; -import com.aliens.backend.global.property.EncodeProperties; +import com.aliens.backend.global.property.PasswordEncodeProperties; import org.springframework.stereotype.Component; import javax.crypto.SecretKeyFactory; @@ -15,15 +15,15 @@ @Component public class PasswordEncoder { - private final EncodeProperties encodeProperties; + private final PasswordEncodeProperties passwordEncodeProperties; - public PasswordEncoder(final EncodeProperties encodeProperties) { - this.encodeProperties = encodeProperties; + public PasswordEncoder(final PasswordEncodeProperties passwordEncodeProperties) { + this.passwordEncodeProperties = passwordEncodeProperties; } public String encrypt(String input) { try { - KeySpec spec = new PBEKeySpec(input.toCharArray(), encodeProperties.getKey(), 85319, 128); + KeySpec spec = new PBEKeySpec(input.toCharArray(), passwordEncodeProperties.getKey(), 85319, 128); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); byte[] hash = factory.generateSecret(spec).getEncoded(); diff --git a/src/main/java/com/aliens/backend/encode/SymmetricKeyEncoder.java b/src/main/java/com/aliens/backend/global/encode/SymmetricKeyEncoder.java similarity index 60% rename from src/main/java/com/aliens/backend/encode/SymmetricKeyEncoder.java rename to src/main/java/com/aliens/backend/global/encode/SymmetricKeyEncoder.java index 47e7b819..f8ddeb27 100644 --- a/src/main/java/com/aliens/backend/encode/SymmetricKeyEncoder.java +++ b/src/main/java/com/aliens/backend/global/encode/SymmetricKeyEncoder.java @@ -1,5 +1,6 @@ -package com.aliens.backend.encode; +package com.aliens.backend.global.encode; +import com.aliens.backend.global.property.SymmetricEncoderProperties; import org.springframework.stereotype.Component; import javax.crypto.Cipher; @@ -9,22 +10,22 @@ @Component public class SymmetricKeyEncoder { - private final EncoderProperties encoderProperties; + private final SymmetricEncoderProperties symmetricEncoderProperties; - public SymmetricKeyEncoder(final EncoderProperties encoderProperties) { - this.encoderProperties = encoderProperties; + public SymmetricKeyEncoder(final SymmetricEncoderProperties symmetricEncoderProperties) { + this.symmetricEncoderProperties = symmetricEncoderProperties; } public String encrypt(String plaintext) throws Exception { Cipher cipher = Cipher.getInstance("AES"); - cipher.init(Cipher.ENCRYPT_MODE, encoderProperties.getEncodeKey()); + cipher.init(Cipher.ENCRYPT_MODE, symmetricEncoderProperties.getEncodeKey()); byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); return Base64.getEncoder().encodeToString(encryptedBytes); } public String decrypt(String ciphertext) throws Exception { Cipher cipher = Cipher.getInstance("AES"); - cipher.init(Cipher.DECRYPT_MODE, encoderProperties.getEncodeKey()); + cipher.init(Cipher.DECRYPT_MODE, symmetricEncoderProperties.getEncodeKey()); byte[] decodedBytes = Base64.getDecoder().decode(ciphertext); byte[] decryptedBytes = cipher.doFinal(decodedBytes); return new String(decryptedBytes, StandardCharsets.UTF_8); diff --git a/src/main/java/com/aliens/backend/global/property/EncodeProperties.java b/src/main/java/com/aliens/backend/global/property/PasswordEncodeProperties.java similarity index 90% rename from src/main/java/com/aliens/backend/global/property/EncodeProperties.java rename to src/main/java/com/aliens/backend/global/property/PasswordEncodeProperties.java index 8e431900..23da99e9 100644 --- a/src/main/java/com/aliens/backend/global/property/EncodeProperties.java +++ b/src/main/java/com/aliens/backend/global/property/PasswordEncodeProperties.java @@ -6,7 +6,7 @@ import java.nio.charset.StandardCharsets; @Component -public class EncodeProperties { +public class PasswordEncodeProperties { @Value("${encode.key}") private String secretKey; diff --git a/src/main/java/com/aliens/backend/encode/EncoderProperties.java b/src/main/java/com/aliens/backend/global/property/SymmetricEncoderProperties.java similarity index 87% rename from src/main/java/com/aliens/backend/encode/EncoderProperties.java rename to src/main/java/com/aliens/backend/global/property/SymmetricEncoderProperties.java index 8d6e1a1e..1ac075ea 100644 --- a/src/main/java/com/aliens/backend/encode/EncoderProperties.java +++ b/src/main/java/com/aliens/backend/global/property/SymmetricEncoderProperties.java @@ -1,4 +1,4 @@ -package com.aliens.backend.encode; +package com.aliens.backend.global.property; import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Value; @@ -8,7 +8,7 @@ import java.nio.charset.StandardCharsets; @Component -public class EncoderProperties { +public class SymmetricEncoderProperties { @Value("${encode.symmetric.key}") private String secretKey; diff --git a/src/test/java/com/aliens/backend/encode/SymmetricKeyEncoderTest.java b/src/test/java/com/aliens/backend/encode/SymmetricKeyEncoderTest.java index 3929f38c..fc62098c 100644 --- a/src/test/java/com/aliens/backend/encode/SymmetricKeyEncoderTest.java +++ b/src/test/java/com/aliens/backend/encode/SymmetricKeyEncoderTest.java @@ -1,5 +1,6 @@ package com.aliens.backend.encode; +import com.aliens.backend.global.encode.SymmetricKeyEncoder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; From 9dbb65eaca2162ca832a5bd43b1561056a06db94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:32:38 +0900 Subject: [PATCH 65/89] =?UTF-8?q?chore(build.gradle)=20:=20JavaMail=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9D=84=20=EC=9C=84=ED=95=B4=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index bea61724..264f2680 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,8 @@ dependencies { //MYSQL runtimeOnly 'com.mysql:mysql-connector-j' + //SMTP + implementation 'org.springframework.boot:spring-boot-starter-mail' } ext { From 10c4f88f3c098d8db7af3bb9a4bbd65649603c1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:33:39 +0900 Subject: [PATCH 66/89] =?UTF-8?q?test(EmailDocTest)=20:=20Email=EA=B3=BC?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=EB=90=9C=20API=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/aliens/backend/docs/EmailDocTest.java | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/test/java/com/aliens/backend/docs/EmailDocTest.java diff --git a/src/test/java/com/aliens/backend/docs/EmailDocTest.java b/src/test/java/com/aliens/backend/docs/EmailDocTest.java new file mode 100644 index 00000000..f4a1d86e --- /dev/null +++ b/src/test/java/com/aliens/backend/docs/EmailDocTest.java @@ -0,0 +1,129 @@ +package com.aliens.backend.docs; + +import com.aliens.backend.email.controller.response.EmailResponse; +import com.aliens.backend.email.service.EmailService; +import com.fasterxml.jackson.databind.ObjectMapper; +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.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.Collections; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.queryParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@AutoConfigureMockMvc +@AutoConfigureRestDocs +@SpringBootTest +class EmailDocTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private EmailService emailService; + + private ObjectMapper objectMapper; + + @BeforeEach + void setUp() { + objectMapper = new ObjectMapper(); + } + + @Test + @DisplayName("이메일 중복 API 테스트 - 사용불가") + void duplicateCheckSuccess() throws Exception { + final String request = "tmp@example.com"; + final String response = EmailResponse.DUPLICATED_EMAIL.getMessage(); + + when(emailService.duplicateCheck(any())).thenReturn(response); + + this.mockMvc.perform(get("/members/exist").param("email", request)) + .andExpect(status().isOk()) + .andDo(document("email-duplicateCheck-success", + queryParameters( + parameterWithName("email").description("검증 요청 이메일") + ), + responseFields( + fieldWithPath("response").description("검증 결과") + ) + )); + } + + @Test + @DisplayName("이메일 중복 API 테스트 - 사용가능") + void duplicateCheckFail() throws Exception { + final String request = "tmp@example.com"; + final String response = EmailResponse.AVAILABLE_EMAIL.getMessage(); + + when(emailService.duplicateCheck(any())).thenReturn(response); + + this.mockMvc.perform(get("/members/exist").param("email", request)) + .andExpect(status().isOk()) + .andDo(document("email-duplicateCheck-fail", + queryParameters( + parameterWithName("email").description("검증 요청 이메일") + ), + responseFields( + fieldWithPath("response").description("검증 결과") + ) + )); + } + + @Test + @DisplayName("인증 이메일 전송 API 테스트") + void sendAuthenticationEmail() throws Exception { + final String email = "tmp@example.com"; + final String response = EmailResponse.EMAIL_SEND_SUCCESS.getMessage(); + + when(emailService.sendAuthenticationEmail(any())).thenReturn(response); + + this.mockMvc.perform(post("/emails/verification/send") + .content(objectMapper.writeValueAsString(Collections.singletonMap("email", email))) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andDo(document("email-verification-send", + requestFields( + fieldWithPath("email").description("검증 요청 이메일") + ), + responseFields( + fieldWithPath("response").description("전송 결과") + ) + )); + } + + @Test + @DisplayName("이메일 검증 요청 API 테스트") + void authenticateEmail() throws Exception { + final String request = "testToken"; + final String response = EmailResponse.EMAIL_AUTHENTICATION_SUCCESS.getMessage(); + + when(emailService.authenticateEmail(any())).thenReturn(response); + + this.mockMvc.perform(get("/emails/confirm").param("token", request)) + .andExpect(status().isOk()) + .andDo(document("email-authentication-success", + queryParameters( + parameterWithName("token").description("이메일 검증을 위한 토큰") + ), + responseFields( + fieldWithPath("response").description("검증 결과") + ) + )); + } +} From eb7e41befe2bbaf8043f21a6f37de03f988b4bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:34:02 +0900 Subject: [PATCH 67/89] =?UTF-8?q?feat(EmailController)=20:=20Email?= =?UTF-8?q?=EA=B3=BC=20=EA=B4=80=EB=A0=A8=EB=90=9C=20API=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../email/controller/EmailController.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/main/java/com/aliens/backend/email/controller/EmailController.java diff --git a/src/main/java/com/aliens/backend/email/controller/EmailController.java b/src/main/java/com/aliens/backend/email/controller/EmailController.java new file mode 100644 index 00000000..091c6f82 --- /dev/null +++ b/src/main/java/com/aliens/backend/email/controller/EmailController.java @@ -0,0 +1,38 @@ +package com.aliens.backend.email.controller; + +import com.aliens.backend.email.service.EmailService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Collections; +import java.util.Map; + +@RestController +public class EmailController { + private final EmailService emailService; + + public EmailController(final EmailService emailService) { + this.emailService = emailService; + } + + @GetMapping("/members/exist") + public ResponseEntity> duplicateCheck(@RequestParam("email") String email) { + String result = emailService.duplicateCheck(email); + Map response = Collections.singletonMap("response", result); + return ResponseEntity.ok(response); + } + + @PostMapping("/emails/verification/send") + public ResponseEntity> sendAuthenticationEmail(@RequestBody String email) throws Exception { + String result = emailService.sendAuthenticationEmail(email); + Map response = Collections.singletonMap("response", result); + return ResponseEntity.ok(response); + } + + @GetMapping("/emails/confirm") + public ResponseEntity> authenticateEmail(@RequestParam("token") String token) throws Exception { + String result = emailService.authenticateEmail(token); + Map response = Collections.singletonMap("response", result); + return ResponseEntity.ok(response); + } +} From 95e22a285f843cceda827ee70e9e8c00383fdbf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:34:28 +0900 Subject: [PATCH 68/89] =?UTF-8?q?feat(EmailResponse)=20:=20Email=EA=B3=BC?= =?UTF-8?q?=20=EA=B4=80=EB=A0=A8=EB=90=9C=20API=20=EA=B2=B0=EA=B3=BC=20Enu?= =?UTF-8?q?m=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/response/EmailResponse.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/com/aliens/backend/email/controller/response/EmailResponse.java diff --git a/src/main/java/com/aliens/backend/email/controller/response/EmailResponse.java b/src/main/java/com/aliens/backend/email/controller/response/EmailResponse.java new file mode 100644 index 00000000..6b7ba988 --- /dev/null +++ b/src/main/java/com/aliens/backend/email/controller/response/EmailResponse.java @@ -0,0 +1,21 @@ +package com.aliens.backend.email.controller.response; + + +public enum EmailResponse { + + DUPLICATED_EMAIL("사용 불가능한 이메일입니다."), + AVAILABLE_EMAIL("사용 가능한 이메일입니다."), + EMAIL_SEND_SUCCESS("이메일 전송 완료"), + EMAIL_AUTHENTICATION_SUCCESS("이메일 인증 완료"), + EMAIL_SEND_FAIL("이메일 전송 실패"); + + private final String message; + + EmailResponse(final String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} \ No newline at end of file From a81451a781f6c1a4e6d16dc6f2fb41bad0c12d26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:34:48 +0900 Subject: [PATCH 69/89] =?UTF-8?q?feat(EmailError)=20:=20Email=EA=B3=BC=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=EB=90=9C=20=EC=98=88=EC=99=B8=20Enum=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/error/EmailError.java | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/error/EmailError.java diff --git a/src/main/java/com/aliens/backend/global/error/EmailError.java b/src/main/java/com/aliens/backend/global/error/EmailError.java new file mode 100644 index 00000000..6d344b8f --- /dev/null +++ b/src/main/java/com/aliens/backend/global/error/EmailError.java @@ -0,0 +1,34 @@ +package com.aliens.backend.global.error; + +import org.springframework.http.HttpStatus; + +public enum EmailError implements ErrorCode { + + NULL_EMAIL(HttpStatus.UNPROCESSABLE_ENTITY, "E1", "해당 EmailAuthentication 엔티티 조회 불가"), + ; + + private final HttpStatus httpStatusCode; + private final String developCode; + private final String message; + + EmailError(final HttpStatus httpStatusCode, final String code, final String message) { + this.httpStatusCode = httpStatusCode; + this.developCode = code; + this.message = message; + } + + @Override + public HttpStatus getHttpStatus() { + return httpStatusCode; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public String getDevelopCode() { + return developCode; + } +} \ No newline at end of file From 3d1c1b80b10b5c245f26a9e69e4b3a77e5bb70b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:35:30 +0900 Subject: [PATCH 70/89] =?UTF-8?q?test(EmailContentTest)=20:=20Email=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=EC=9D=84=20=EA=B0=80=EC=A7=84=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../email/sender/EmailContentTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/test/java/com/aliens/backend/email/sender/EmailContentTest.java diff --git a/src/test/java/com/aliens/backend/email/sender/EmailContentTest.java b/src/test/java/com/aliens/backend/email/sender/EmailContentTest.java new file mode 100644 index 00000000..defae0b3 --- /dev/null +++ b/src/test/java/com/aliens/backend/email/sender/EmailContentTest.java @@ -0,0 +1,39 @@ +package com.aliens.backend.email.sender; + +import com.aliens.backend.email.service.EmailContent; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class EmailContentTest { + + @Autowired + EmailContent emailContent; + + @Test + @DisplayName("메일 내용 가져오기") + void getContentTest() { + //Given + String token = "tmpToken"; + String domainURL = "tmp.com"; + + //When + String result = emailContent.getContent(token, domainURL); + + //Then + Assertions.assertNotNull(result); + } + + @Test + @DisplayName("메일 제목 가져오기") + void getTitleTest() { + //When + String result = emailContent.getTitle(); + + //Then + Assertions.assertNotNull(result); + } +} From 5dd2a9d01af87813af4c7fa2071b9f7db0320eff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:35:48 +0900 Subject: [PATCH 71/89] =?UTF-8?q?feat(EmailContent)=20:=20Email=20?= =?UTF-8?q?=EB=82=B4=EC=9A=A9=EC=9D=84=20=EA=B0=80=EC=A7=84=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/email/service/EmailContent.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/main/java/com/aliens/backend/email/service/EmailContent.java diff --git a/src/main/java/com/aliens/backend/email/service/EmailContent.java b/src/main/java/com/aliens/backend/email/service/EmailContent.java new file mode 100644 index 00000000..3cde1922 --- /dev/null +++ b/src/main/java/com/aliens/backend/email/service/EmailContent.java @@ -0,0 +1,41 @@ +package com.aliens.backend.email.service; + +import org.springframework.stereotype.Component; + +@Component +public class EmailContent { + + public String getTitle() { + return "[FriendShip] Account Verification 회원가입 이메일 인증"; + } + + public String getContent(String token, String domainUrl) { + String englishContent = "Thank you for registering with FriendShip App.\n" + + "To ensure the security of your account, we need to verify your email address.\n\n" + + "Please click the link below to verify your email and activate your account:\n" + + "{0}email/confirm?token={1}\n\n" + + "If you are unable to click the link,\n" + + "you can copy and paste the following URL into your browser\n\n" + + "If you did not sign up for an account with FriendShip App, please ignore this email.\n" + + "Your information will not be used for any unauthorized purposes.\n\n" + + "Thank you for choosing FriendShip App.\n" + + "If you have any questions or concerns, please contact our support team at this email.\n\n" + + "Best regards,\n" + + "The 4Aliens Team\n\n\n\n\n"; + + String koreanContent = "안녕하세요!\n\n" + + "FriendShip App에 가입해 주셔서 감사합니다.\n" + + "서비스를 사용하시기 위해서는 계정의 보안을 위해 이메일 주소를 인증해야 합니다.\n\n" + + "아래 링크를 클릭하여 이메일 주소를 인증하고 계정을 활성화해 주세요:\n" + + "{0}email/confirm?token={1}\n\n" + + "링크를 클릭할 수 없는 경우 다음 URL을 브라우저에 복사하여 붙여넣으셔도 됩니다.\n\n" + + "만약 FriendShip App에 계정을 만든 적이 없는 경우,\n" + + "이 이메일을 무시해 주세요. 귀하의 정보는 무단으로 사용되지 않습니다.\n\n" + + "FriendShip App을 선택해 주셔서 감사합니다.\n" + + "궁금한 점이나 문의사항이 있으시면 해당 이메일로 문의해 주세요.\n\n" + + "감사합니다,\n" + + "4Aliens 팀 올림\n"; + + return englishContent + koreanContent; + } +} From 1ed646e693f0e2e1efbce842b4241984588c9079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:36:47 +0900 Subject: [PATCH 72/89] =?UTF-8?q?feat(MailConfig)=20:=20javaMail=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20Bean=20=EC=A3=BC=EC=9E=85=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4,=20=ED=95=84=EC=9A=94=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=20=EC=82=BD=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/config/MailConfig.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/config/MailConfig.java diff --git a/src/main/java/com/aliens/backend/global/config/MailConfig.java b/src/main/java/com/aliens/backend/global/config/MailConfig.java new file mode 100644 index 00000000..4b56cda8 --- /dev/null +++ b/src/main/java/com/aliens/backend/global/config/MailConfig.java @@ -0,0 +1,39 @@ +package com.aliens.backend.global.config; + +import com.aliens.backend.global.property.EmailProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; + +import java.util.Properties; + +@Configuration +public class MailConfig { + + private final EmailProperties emailProperties; + + public MailConfig(final EmailProperties emailProperties) { + this.emailProperties = emailProperties; + } + + @Bean + public JavaMailSender javaMailService() { + JavaMailSenderImpl jms = new JavaMailSenderImpl(); + jms.setHost(emailProperties.getHost()); + jms.setPort(emailProperties.getPort()); + jms.setUsername(emailProperties.getEmail()); + jms.setPassword(emailProperties.getPassword()); + jms.setJavaMailProperties(getMailProperties()); + return jms; + } + + private Properties getMailProperties() { + Properties prop = new Properties(); + prop.setProperty("mail.transport.protocol", "smtp"); + prop.setProperty("mail.smtp.auth", "true"); + prop.setProperty("mail.smtp.starttls.enable", "true"); + prop.setProperty("mail.debug", "true"); + return prop; + } +} \ No newline at end of file From c9233e627cad0043c5e9107d0b5f0baf00c24ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:37:14 +0900 Subject: [PATCH 73/89] =?UTF-8?q?feat(EmailProperties)=20:=20javaMail=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1=EC=9D=84=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/property/EmailProperties.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/main/java/com/aliens/backend/global/property/EmailProperties.java diff --git a/src/main/java/com/aliens/backend/global/property/EmailProperties.java b/src/main/java/com/aliens/backend/global/property/EmailProperties.java new file mode 100644 index 00000000..cb15afb3 --- /dev/null +++ b/src/main/java/com/aliens/backend/global/property/EmailProperties.java @@ -0,0 +1,44 @@ +package com.aliens.backend.global.property; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + + +@Component +public class EmailProperties { + + @Value("${domain.url}") + private String domainUrl; + + @Value("${smtp.email}") + private String email; + + @Value("${smtp.password}") + private String password; + + @Value("${smtp.host}") + private String host; + + @Value("${smtp.port}") + private int port; + + public String getDomainUrl() { + return domainUrl; + } + + public String getEmail() { + return email; + } + + public String getPassword() { + return password; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } +} From 7cf028d3f664255be281ecaccc91b1ce2ba5641a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:37:28 +0900 Subject: [PATCH 74/89] =?UTF-8?q?feat(EmailSenderTest)=20:=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=20=EC=A0=84=EC=86=A1=20=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/email/sender/EmailSenderTest.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/test/java/com/aliens/backend/email/sender/EmailSenderTest.java diff --git a/src/test/java/com/aliens/backend/email/sender/EmailSenderTest.java b/src/test/java/com/aliens/backend/email/sender/EmailSenderTest.java new file mode 100644 index 00000000..67210fbd --- /dev/null +++ b/src/test/java/com/aliens/backend/email/sender/EmailSenderTest.java @@ -0,0 +1,36 @@ +package com.aliens.backend.email.sender; + +import com.aliens.backend.email.service.EmailSender; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatchers; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@SpringBootTest +class EmailSenderTest { + + @Autowired + EmailSender emailSender; + + @Test + @DisplayName("인증 이메일 전송 테스트") + void sendAuthenticationEmailTest() { + //Given + String email = "tmp@example.com"; + String token = "1"; + JavaMailSender javaMailSender = mock(JavaMailSender.class); + emailSender.setJavaMailSender(javaMailSender); + + //When + emailSender.sendAuthenticationEmail(email, token); + + //Then + verify(javaMailSender).send(ArgumentMatchers.any(SimpleMailMessage.class)); + } +} From 36f85c33eb1fc7b6b7f6484379e2004fa8d05a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:37:42 +0900 Subject: [PATCH 75/89] =?UTF-8?q?feat(EmailSender)=20:=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=20=EC=A0=84=EC=86=A1=20=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/email/service/EmailSender.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/main/java/com/aliens/backend/email/service/EmailSender.java diff --git a/src/main/java/com/aliens/backend/email/service/EmailSender.java b/src/main/java/com/aliens/backend/email/service/EmailSender.java new file mode 100644 index 00000000..9966085b --- /dev/null +++ b/src/main/java/com/aliens/backend/email/service/EmailSender.java @@ -0,0 +1,41 @@ +package com.aliens.backend.email.service; + +import com.aliens.backend.global.property.EmailProperties; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.stereotype.Component; + +@Component +public class EmailSender { + + private final EmailContent emailContent; + private final EmailProperties emailProperties; + private JavaMailSender javaMailSender; + + public EmailSender(final EmailContent emailContent, + final EmailProperties emailProperties, + final JavaMailSender javaMailSender) { + this.emailContent = emailContent; + this.emailProperties = emailProperties; + this.javaMailSender = javaMailSender; + } + + public void setJavaMailSender(final JavaMailSender javaMailSender) { + this.javaMailSender = javaMailSender; + } + + public void sendAuthenticationEmail(final String email, final String emailToken) { + SimpleMailMessage mailMessage = createAuthenticationMail(email, emailToken); + javaMailSender.send(mailMessage); + } + + private SimpleMailMessage createAuthenticationMail(final String email, final String emailToken) { + SimpleMailMessage authenticationEmail = new SimpleMailMessage(); + authenticationEmail.setTo(email); + authenticationEmail.setSubject(emailContent.getTitle()); + + String content = emailContent.getContent(emailToken, emailProperties.getDomainUrl()); + authenticationEmail.setText(content); + return authenticationEmail; + } +} From 2d9ccbd67a759eabc71c48609fff2a368e937093 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:37:53 +0900 Subject: [PATCH 76/89] =?UTF-8?q?feat(EmailServiceTest)=20:=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=20=EC=84=9C=EB=B9=84=EC=8A=A4=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../email/service/EmailServiceTest.java | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/test/java/com/aliens/backend/email/service/EmailServiceTest.java diff --git a/src/test/java/com/aliens/backend/email/service/EmailServiceTest.java b/src/test/java/com/aliens/backend/email/service/EmailServiceTest.java new file mode 100644 index 00000000..b47d90ca --- /dev/null +++ b/src/test/java/com/aliens/backend/email/service/EmailServiceTest.java @@ -0,0 +1,85 @@ +package com.aliens.backend.email.service; + +import com.aliens.backend.email.controller.response.EmailResponse; +import com.aliens.backend.email.domain.EmailAuthentication; +import com.aliens.backend.email.domain.repository.EmailAuthenticationRepository; +import com.aliens.backend.global.encode.SymmetricKeyEncoder; +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class EmailServiceTest { + + @Autowired + EmailService emailService; + @Autowired + SymmetricKeyEncoder symmetricKeyEncoder; + @Autowired + EmailAuthenticationRepository emailAuthenticationRepository; + + String givenEmail; + EmailAuthentication emailEntity; + + @BeforeEach + void setUp() { + givenEmail = "tmp@example.com"; + emailEntity = new EmailAuthentication(givenEmail); + emailAuthenticationRepository.save(emailEntity); + } + + @AfterEach + void afterDown() { + emailAuthenticationRepository.deleteAll(); + } + + @Test + @DisplayName("이메일 중복 검사 - 사용 불가한 이메일") + void duplicateCheckTestNotAvailable() { + //When + String result = emailService.duplicateCheck(givenEmail); + + //Then + Assertions.assertEquals(EmailResponse.DUPLICATED_EMAIL.getMessage(), result); + } + + @Test + @DisplayName("이메일 중복 검사 - 사용 가능한 이메일") + void duplicateCheckTestAvailable() { + //Given + String newEmail = "newEmail@example.com"; + + //When + String result = emailService.duplicateCheck(newEmail); + + //Then + Assertions.assertEquals(EmailResponse.AVAILABLE_EMAIL.getMessage(), result); + } + + @Test + @DisplayName("검증 이메일 전송") + void sendAuthenticationEmailTest() throws Exception { + //Given + String newEmail = "newEmail@example.com"; + + //When + String result = emailService.sendAuthenticationEmail(newEmail); + + //Then + Assertions.assertEquals(EmailResponse.EMAIL_SEND_SUCCESS.getMessage(), result); + } + + @Test + @DisplayName("이메일 검증") + void authenticateEmailTest() throws Exception { + //Given + Long entityId = emailAuthenticationRepository.save(emailEntity).getId(); + String token = symmetricKeyEncoder.encrypt(String.valueOf(entityId)); + + //When + String result = emailService.authenticateEmail(token); + + //Then + Assertions.assertEquals(EmailResponse.EMAIL_AUTHENTICATION_SUCCESS.getMessage(), result); + } +} From 2f7b682bc43460ddfc23c1d6ee4ee63bd2eaab56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:38:49 +0900 Subject: [PATCH 77/89] =?UTF-8?q?feat(EmailAuthentication)=20:=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=EC=9D=84=20=EC=A0=80=EC=9E=A5=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../email/domain/EmailAuthentication.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/main/java/com/aliens/backend/email/domain/EmailAuthentication.java diff --git a/src/main/java/com/aliens/backend/email/domain/EmailAuthentication.java b/src/main/java/com/aliens/backend/email/domain/EmailAuthentication.java new file mode 100644 index 00000000..4e18f23e --- /dev/null +++ b/src/main/java/com/aliens/backend/email/domain/EmailAuthentication.java @@ -0,0 +1,37 @@ +package com.aliens.backend.email.domain; + +import jakarta.persistence.*; + +@Entity +@Table(schema = "EMAIL_AUTHENTICATION") +public class EmailAuthentication { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column + private Long id; + + @Column + private String email; + + @Column(name = "is_authenticated") + private boolean isAuthenticated = false; + + protected EmailAuthentication() { + } + + public EmailAuthentication(String email) { + this.email = email; + } + + public boolean isAuthenticated() { + return isAuthenticated; + } + + public Long getId() { + return id; + } + + public void authenticate() { + isAuthenticated = true; + } +} From e8fb40c70b5aa80dc87b763b7445fcb7bf201d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:38:57 +0900 Subject: [PATCH 78/89] =?UTF-8?q?feat(EmailAuthenticationRepository)=20:?= =?UTF-8?q?=20=EC=9D=B8=EC=A6=9D=EC=9D=B4=EB=A9=94=EC=9D=BC=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=B4=20DB=20=EC=A0=91=EA=B7=BC=ED=95=98=EB=8A=94?= =?UTF-8?q?=20=EB=A0=88=ED=8C=8C=EC=A7=80=ED=86=A0=EB=A6=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/EmailAuthenticationRepository.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/com/aliens/backend/email/domain/repository/EmailAuthenticationRepository.java diff --git a/src/main/java/com/aliens/backend/email/domain/repository/EmailAuthenticationRepository.java b/src/main/java/com/aliens/backend/email/domain/repository/EmailAuthenticationRepository.java new file mode 100644 index 00000000..e231bb91 --- /dev/null +++ b/src/main/java/com/aliens/backend/email/domain/repository/EmailAuthenticationRepository.java @@ -0,0 +1,12 @@ +package com.aliens.backend.email.domain.repository; + +import com.aliens.backend.email.domain.EmailAuthentication; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface EmailAuthenticationRepository extends JpaRepository { + Optional findByEmail(String email); +} From df7647613c10e1075b08320d39db66b7b87fe1f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 15:39:36 +0900 Subject: [PATCH 79/89] =?UTF-8?q?feat(EmailService)=20:=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=20=EC=A4=91=EB=B3=B5=20=EA=B2=80=EC=82=AC,?= =?UTF-8?q?=20=EC=9D=B8=EC=A6=9D=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1,=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=9D=B8=EC=A6=9D?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/email/service/EmailService.java | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/main/java/com/aliens/backend/email/service/EmailService.java diff --git a/src/main/java/com/aliens/backend/email/service/EmailService.java b/src/main/java/com/aliens/backend/email/service/EmailService.java new file mode 100644 index 00000000..e4c20697 --- /dev/null +++ b/src/main/java/com/aliens/backend/email/service/EmailService.java @@ -0,0 +1,61 @@ +package com.aliens.backend.email.service; + +import com.aliens.backend.email.controller.response.EmailResponse; +import com.aliens.backend.email.domain.EmailAuthentication; +import com.aliens.backend.email.domain.repository.EmailAuthenticationRepository; +import com.aliens.backend.global.encode.SymmetricKeyEncoder; +import com.aliens.backend.global.error.EmailError; +import com.aliens.backend.global.exception.RestApiException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Optional; + +@Service +public class EmailService { + + private final EmailAuthenticationRepository emailAuthenticationRepository; + private final EmailSender emailSender; + private final SymmetricKeyEncoder symmetricKeyEncoder; + + public EmailService(final EmailAuthenticationRepository emailRepository, + final EmailSender emailSender, + final SymmetricKeyEncoder symmetricKeyEncoder) { + this.emailAuthenticationRepository = emailRepository; + this.emailSender = emailSender; + this.symmetricKeyEncoder = symmetricKeyEncoder; + } + + @Transactional(readOnly = true) + public String duplicateCheck(final String email) { + Optional emailAuthentication = emailAuthenticationRepository.findByEmail(email); + + if (emailAuthentication.isPresent()) { + return EmailResponse.DUPLICATED_EMAIL.getMessage(); + } + return EmailResponse.AVAILABLE_EMAIL.getMessage(); + } + + @Transactional + public String sendAuthenticationEmail(final String email) throws Exception { + EmailAuthentication emailEntity = new EmailAuthentication(email); + emailAuthenticationRepository.save(emailEntity); + + String emailToken = symmetricKeyEncoder.encrypt(String.valueOf(emailEntity.getId())); + emailSender.sendAuthenticationEmail(email, emailToken); + + return EmailResponse.EMAIL_SEND_SUCCESS.getMessage(); + } + + @Transactional + public String authenticateEmail(final String token) throws Exception { + Long emailEntityId = Long.valueOf(symmetricKeyEncoder.decrypt(token)); + EmailAuthentication emailEntity = getEmailAuthentication(emailEntityId); + emailEntity.authenticate(); + return EmailResponse.EMAIL_AUTHENTICATION_SUCCESS.getMessage(); + } + + private EmailAuthentication getEmailAuthentication(final Long emailEntityId) { + return emailAuthenticationRepository.findById(emailEntityId).orElseThrow(() -> new RestApiException(EmailError.NULL_EMAIL)); + } +} From dc1932871adc367652e3be4ca502e05bba5b6858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 18:27:51 +0900 Subject: [PATCH 80/89] =?UTF-8?q?refactor(AwsS3Uploader,AwsS3UploaderTest)?= =?UTF-8?q?=20:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B0=A9=EC=8B=9D=EB=B3=80=EA=B2=BD<=EB=A1=9C?= =?UTF-8?q?=EC=BB=AC=20IO=20=EC=9E=91=EC=97=85=20=EC=82=AD=EC=A0=9C>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/uploader/AwsS3Uploader.java | 64 +++++++------------ .../backend/uploader/AwsS3UploaderTest.java | 14 ++-- 2 files changed, 31 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/aliens/backend/uploader/AwsS3Uploader.java b/src/main/java/com/aliens/backend/uploader/AwsS3Uploader.java index fe067b79..3149da73 100644 --- a/src/main/java/com/aliens/backend/uploader/AwsS3Uploader.java +++ b/src/main/java/com/aliens/backend/uploader/AwsS3Uploader.java @@ -1,16 +1,14 @@ package com.aliens.backend.uploader; import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.model.CannedAccessControlList; -import com.amazonaws.services.s3.model.PutObjectRequest; +import com.amazonaws.services.s3.model.ObjectMetadata; +import org.springframework.http.MediaType; import org.springframework.stereotype.Component; import org.springframework.web.multipart.MultipartFile; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.util.Optional; +import java.util.List; import java.util.UUID; @Component @@ -18,51 +16,35 @@ public class AwsS3Uploader { private final AmazonS3Client amazonS3Client; private final S3UploadProperties s3UploadProperties; - private final LocalUploadProperties localUploadProperties; - public AwsS3Uploader(final AmazonS3Client amazonS3Client, final S3UploadProperties s3UploadProperties, final LocalUploadProperties localUploadProperties) { + private static final String SUFFIX = ".png"; + + public AwsS3Uploader(final AmazonS3Client amazonS3Client, final S3UploadProperties s3UploadProperties) { this.amazonS3Client = amazonS3Client; this.s3UploadProperties = s3UploadProperties; - this.localUploadProperties = localUploadProperties; } - public String upload(MultipartFile multipartFile) throws IOException { - File uploadFile = convert(multipartFile) - .orElseThrow(() -> new IllegalArgumentException("MultipartFile -> File convert fail")); - - return uploadToS3(uploadFile); + public List upload(UploadFileRequest request) { + return request.files().parallelStream().map(this::uploadToS3).toList(); } - //로컬에 파일생성 - private Optional convert(MultipartFile file) throws IOException { - File convertFile = new File(file.getOriginalFilename()); - if (convertFile.createNewFile()) { - try (FileOutputStream fos = new FileOutputStream(convertFile)) { - fos.write(file.getBytes()); - } - return Optional.of(convertFile); + private S3File uploadToS3(MultipartFile multipartFile) { + String uuidName = UUID.randomUUID() + SUFFIX; + + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentType(MediaType.IMAGE_PNG_VALUE); + metadata.setContentLength(multipartFile.getSize()); + try { + amazonS3Client.putObject( + s3UploadProperties.getBucket(), + uuidName, + multipartFile.getInputStream(), + metadata); + }catch (IOException e) { + throw new RuntimeException("업로드 실패"); } - return Optional.empty(); - } - - private String uploadToS3(File uploadFile) { - String fileName = new StringBuilder() - .append(localUploadProperties.getUploadPath()).append("/") - .append(UUID.randomUUID()) - .append(uploadFile.getName()).toString(); - - String uploadImageUrl = putS3(uploadFile, fileName); // S3로 업로드 - uploadFile.delete(); // 로컬 파일 삭제 - return uploadImageUrl; - } - - // S3에 업로드 - private String putS3(File uploadFile, String fileName) { - amazonS3Client.putObject(new PutObjectRequest(s3UploadProperties.getBucket(), - fileName, uploadFile) - .withCannedAcl(CannedAccessControlList.PublicRead)); - return amazonS3Client.getUrl(s3UploadProperties.getBucket(),fileName).toString(); + return new S3File(uuidName,amazonS3Client.getUrl(s3UploadProperties.getBucket(),uuidName).toString()); } public boolean delete(String fileName) { diff --git a/src/test/java/com/aliens/backend/uploader/AwsS3UploaderTest.java b/src/test/java/com/aliens/backend/uploader/AwsS3UploaderTest.java index 9d63685f..bf1ad5be 100644 --- a/src/test/java/com/aliens/backend/uploader/AwsS3UploaderTest.java +++ b/src/test/java/com/aliens/backend/uploader/AwsS3UploaderTest.java @@ -10,7 +10,7 @@ import org.springframework.context.annotation.Import; import org.springframework.mock.web.MockMultipartFile; -import java.io.IOException; +import java.util.List; @Import(AwsS3MockConfig.class) @SpringBootTest @@ -29,27 +29,29 @@ public void tearDown() { @Test @DisplayName("S3 파일 업로드 테스트") - void uploadTest() throws IOException { + void uploadTest() { // Given String path = "test.png"; String contentType = "image/png"; MockMultipartFile file = new MockMultipartFile("test", path, contentType, "test".getBytes()); + UploadFileRequest request = new UploadFileRequest(List.of(file,file,file)); + // When - String urlPath = awsS3Uploader.upload(file); + List S3Files = awsS3Uploader.upload(request); // Then - Assertions.assertThat(urlPath).contains(path); + Assertions.assertThat(S3Files).isNotNull(); } @Test @DisplayName("S3 파일 삭제 테스트") void deleteTest() { // Given - String path = "test.png"; + String fileName = "test.png"; // When - boolean result = awsS3Uploader.delete(path); + boolean result = awsS3Uploader.delete(fileName); // Then org.junit.jupiter.api.Assertions.assertTrue(result); From 0803ccb370d4f1aa73c30f0315f34e76f43b6cfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 18:28:12 +0900 Subject: [PATCH 81/89] =?UTF-8?q?remove(LocalUploadProperties)=20:=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=97=85=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=EB=B3=80=EA=B2=BD=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../uploader/LocalUploadProperties.java | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 src/main/java/com/aliens/backend/uploader/LocalUploadProperties.java diff --git a/src/main/java/com/aliens/backend/uploader/LocalUploadProperties.java b/src/main/java/com/aliens/backend/uploader/LocalUploadProperties.java deleted file mode 100644 index 645a0481..00000000 --- a/src/main/java/com/aliens/backend/uploader/LocalUploadProperties.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.aliens.backend.uploader; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -@Component -public class LocalUploadProperties { - - @Value("${spring.servlet.multipart.location}") - private String uploadPath; - - @Value("${spring.servlet.multipart.enabled}") - private boolean available; - - @Value("${spring.servlet.multipart.max-request-size}") - private String maxRequestSize; - - @Value("${spring.servlet.multipart.max-file-size}") - private String maxFileSize; - - public String getUploadPath() { - return uploadPath; - } -} From 22a05dc575635057a2351cb49b7020037247bc74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 18:28:50 +0900 Subject: [PATCH 82/89] =?UTF-8?q?feat(S3File)=20:=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=EB=90=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=EA=B3=BC,=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=EC=9A=A9=20URL=EC=9D=84=20=EB=8B=B4=EC=9D=80=20DTO=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/aliens/backend/uploader/S3File.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/com/aliens/backend/uploader/S3File.java diff --git a/src/main/java/com/aliens/backend/uploader/S3File.java b/src/main/java/com/aliens/backend/uploader/S3File.java new file mode 100644 index 00000000..01da091a --- /dev/null +++ b/src/main/java/com/aliens/backend/uploader/S3File.java @@ -0,0 +1,4 @@ +package com.aliens.backend.uploader; + +public record S3File(String fileName, String fileURL) { +} From 6f4b70436dc44c82f8271f9e3ffb353dbf237469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 21:51:01 +0900 Subject: [PATCH 83/89] =?UTF-8?q?refactor(EmailDocTest)=20:=20RestDoc=20?= =?UTF-8?q?=EC=A0=84=EB=8B=AC=EB=90=98=EB=8A=94=20=EC=8A=A4=EB=8B=99?= =?UTF-8?q?=ED=8C=BB=20=EC=A0=95=EB=B3=B4=20=EB=84=A4=EC=9D=B4=EB=B0=8D=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 --- src/test/java/com/aliens/backend/docs/EmailDocTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/aliens/backend/docs/EmailDocTest.java b/src/test/java/com/aliens/backend/docs/EmailDocTest.java index f4a1d86e..0a159b5c 100644 --- a/src/test/java/com/aliens/backend/docs/EmailDocTest.java +++ b/src/test/java/com/aliens/backend/docs/EmailDocTest.java @@ -55,7 +55,7 @@ void duplicateCheckSuccess() throws Exception { this.mockMvc.perform(get("/members/exist").param("email", request)) .andExpect(status().isOk()) - .andDo(document("email-duplicateCheck-success", + .andDo(document("email-duplicateCheck-fail", queryParameters( parameterWithName("email").description("검증 요청 이메일") ), @@ -75,7 +75,7 @@ void duplicateCheckFail() throws Exception { this.mockMvc.perform(get("/members/exist").param("email", request)) .andExpect(status().isOk()) - .andDo(document("email-duplicateCheck-fail", + .andDo(document("email-duplicateCheck-success", queryParameters( parameterWithName("email").description("검증 요청 이메일") ), From 7a95b6b680a79b6e940cc0a64a861e13dee81746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 21:51:30 +0900 Subject: [PATCH 84/89] =?UTF-8?q?refactor:=20Authentication=20=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20doc=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/{index.adoc => authentication.adoc} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/docs/asciidoc/{index.adoc => authentication.adoc} (100%) diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/authentication.adoc similarity index 100% rename from src/docs/asciidoc/index.adoc rename to src/docs/asciidoc/authentication.adoc From b7c686b79a907682dd1d84826e7a69cff1005825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 21:51:48 +0900 Subject: [PATCH 85/89] =?UTF-8?q?feat(email.adoc)=20:=20email=20=EC=9D=84?= =?UTF-8?q?=20=EC=9C=84=ED=95=9C=20doc=EC=9C=BC=EB=A1=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/email.adoc | 75 ++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 src/docs/asciidoc/email.adoc diff --git a/src/docs/asciidoc/email.adoc b/src/docs/asciidoc/email.adoc new file mode 100644 index 00000000..a07d118b --- /dev/null +++ b/src/docs/asciidoc/email.adoc @@ -0,0 +1,75 @@ += Spring REST Docs +:toc: left +:toclevels: 2 +:sectlinks: + +[[resources-post]] +== 이메일 인증 + +[[resources-post-create]] +=== 이메일 중복 검사 - 사용가능 + +==== HTTP request + +include::{snippets}/email-duplicateCheck-success/http-request.adoc[] + +==== request-param 설명 +include::{snippets}/email-duplicateCheck-success/query-parameters.adoc[] + +==== HTTP response + +include::{snippets}/email-duplicateCheck-success/http-response.adoc[] + +==== response-body 설명 +include::{snippets}/email-duplicateCheck-success/response-fields.adoc[] + + + +=== 이메일 중복 검사 - 사용불가 + +==== HTTP request + +include::{snippets}/email-duplicateCheck-fail/http-request.adoc[] + +==== request-param 설명 +include::{snippets}/email-duplicateCheck-fail/query-parameters.adoc[] + +==== HTTP response + +include::{snippets}/email-duplicateCheck-fail/http-response.adoc[] + + + + +=== 인증 이메일 전송 + +==== HTTP request + +include::{snippets}/email-verification-send/http-request.adoc[] + +==== request-body 설명 +include::{snippets}/email-verification-send/request-fields.adoc[] + +==== HTTP response + +include::{snippets}/email-verification-send/http-response.adoc[] + +==== response-body 설명 +include::{snippets}/email-verification-send/response-fields.adoc[] + + +=== 인증 이메일 검증 요청 + +==== HTTP request + +include::{snippets}/email-authentication-success/http-request.adoc[] + +==== request-param 설명 +include::{snippets}/email-authentication-success/query-parameters.adoc[] + +==== HTTP response + +include::{snippets}/email-authentication-success/http-response.adoc[] + +==== response-body 설명 +include::{snippets}/email-authentication-success/response-fields.adoc[] From 4ac8efe93b0083c2cc813ae05828dae430138ae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:30:09 +0900 Subject: [PATCH 86/89] =?UTF-8?q?test(EmailSenderTest)=20:=20JavaMailSende?= =?UTF-8?q?r=EB=A5=BC=20Mock=EC=9C=BC=EB=A1=9C=20=EB=8C=80=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/email/sender/EmailSenderTest.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/aliens/backend/email/sender/EmailSenderTest.java b/src/test/java/com/aliens/backend/email/sender/EmailSenderTest.java index 67210fbd..b97cd4e4 100644 --- a/src/test/java/com/aliens/backend/email/sender/EmailSenderTest.java +++ b/src/test/java/com/aliens/backend/email/sender/EmailSenderTest.java @@ -6,6 +6,7 @@ import org.mockito.ArgumentMatchers; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; @@ -16,21 +17,22 @@ class EmailSenderTest { @Autowired - EmailSender emailSender; + private EmailSender emailSender; + + @MockBean + private JavaMailSender javaMailSender; @Test @DisplayName("인증 이메일 전송 테스트") void sendAuthenticationEmailTest() { - //Given + // Given String email = "tmp@example.com"; String token = "1"; - JavaMailSender javaMailSender = mock(JavaMailSender.class); - emailSender.setJavaMailSender(javaMailSender); - //When + // When emailSender.sendAuthenticationEmail(email, token); - //Then + // Then verify(javaMailSender).send(ArgumentMatchers.any(SimpleMailMessage.class)); } } From e7b460c50616904b44089b20b022693eb46da558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:30:52 +0900 Subject: [PATCH 87/89] =?UTF-8?q?refactor(EmailSender)=20:=20JavaMailSende?= =?UTF-8?q?r=EB=A5=BC=20Set=20=ED=95=B4=EC=A3=BC=EB=8A=94=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/aliens/backend/email/service/EmailSender.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/aliens/backend/email/service/EmailSender.java b/src/main/java/com/aliens/backend/email/service/EmailSender.java index 9966085b..66565afb 100644 --- a/src/main/java/com/aliens/backend/email/service/EmailSender.java +++ b/src/main/java/com/aliens/backend/email/service/EmailSender.java @@ -20,10 +20,6 @@ public EmailSender(final EmailContent emailContent, this.javaMailSender = javaMailSender; } - public void setJavaMailSender(final JavaMailSender javaMailSender) { - this.javaMailSender = javaMailSender; - } - public void sendAuthenticationEmail(final String email, final String emailToken) { SimpleMailMessage mailMessage = createAuthenticationMail(email, emailToken); javaMailSender.send(mailMessage); From a4c14553d9437d1c7dd8b2d13ded9d6fa03efd6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:34:52 +0900 Subject: [PATCH 88/89] =?UTF-8?q?style(EmailDocTest,=20email.=20)=20:=20do?= =?UTF-8?q?c=EC=97=90=20=EC=A0=81=EC=9A=A9=EB=90=98=EB=8A=94=20=EC=8A=A4?= =?UTF-8?q?=EB=8B=99=ED=8C=BB=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/docs/asciidoc/email.adoc | 14 +++++++------- .../java/com/aliens/backend/docs/EmailDocTest.java | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/docs/asciidoc/email.adoc b/src/docs/asciidoc/email.adoc index a07d118b..aef57550 100644 --- a/src/docs/asciidoc/email.adoc +++ b/src/docs/asciidoc/email.adoc @@ -11,17 +11,17 @@ ==== HTTP request -include::{snippets}/email-duplicateCheck-success/http-request.adoc[] +include::{snippets}/email-duplicateCheck-available/http-request.adoc[] ==== request-param 설명 -include::{snippets}/email-duplicateCheck-success/query-parameters.adoc[] +include::{snippets}/email-duplicateCheck-available/query-parameters.adoc[] ==== HTTP response -include::{snippets}/email-duplicateCheck-success/http-response.adoc[] +include::{snippets}/email-duplicateCheck-available/http-response.adoc[] ==== response-body 설명 -include::{snippets}/email-duplicateCheck-success/response-fields.adoc[] +include::{snippets}/email-duplicateCheck-available/response-fields.adoc[] @@ -29,14 +29,14 @@ include::{snippets}/email-duplicateCheck-success/response-fields.adoc[] ==== HTTP request -include::{snippets}/email-duplicateCheck-fail/http-request.adoc[] +include::{snippets}/email-duplicateCheck-duplicate/http-request.adoc[] ==== request-param 설명 -include::{snippets}/email-duplicateCheck-fail/query-parameters.adoc[] +include::{snippets}/email-duplicateCheck-duplicate/query-parameters.adoc[] ==== HTTP response -include::{snippets}/email-duplicateCheck-fail/http-response.adoc[] +include::{snippets}/email-duplicateCheck-duplicate/http-response.adoc[] diff --git a/src/test/java/com/aliens/backend/docs/EmailDocTest.java b/src/test/java/com/aliens/backend/docs/EmailDocTest.java index 0a159b5c..23659462 100644 --- a/src/test/java/com/aliens/backend/docs/EmailDocTest.java +++ b/src/test/java/com/aliens/backend/docs/EmailDocTest.java @@ -55,7 +55,7 @@ void duplicateCheckSuccess() throws Exception { this.mockMvc.perform(get("/members/exist").param("email", request)) .andExpect(status().isOk()) - .andDo(document("email-duplicateCheck-fail", + .andDo(document("email-duplicateCheck-duplicate", queryParameters( parameterWithName("email").description("검증 요청 이메일") ), @@ -75,7 +75,7 @@ void duplicateCheckFail() throws Exception { this.mockMvc.perform(get("/members/exist").param("email", request)) .andExpect(status().isOk()) - .andDo(document("email-duplicateCheck-success", + .andDo(document("email-duplicateCheck-available", queryParameters( parameterWithName("email").description("검증 요청 이메일") ), From 184aa4f01724cfff3a2ca20a0b1e496d51c51df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AA=85=EC=A4=80?= <86913355+mjj111@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:42:06 +0900 Subject: [PATCH 89/89] =?UTF-8?q?refactor(EmailProperties,=20MailConfig)?= =?UTF-8?q?=20:=20JavaMailSender=20=EC=84=A4=EC=A0=95=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EC=97=90=20=ED=95=98=EB=93=9C=EC=BD=94=EB=94=A9=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EA=B2=83=EB=93=A4=20yml=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/config/MailConfig.java | 8 +++--- .../global/property/EmailProperties.java | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/aliens/backend/global/config/MailConfig.java b/src/main/java/com/aliens/backend/global/config/MailConfig.java index 4b56cda8..c54eb8e0 100644 --- a/src/main/java/com/aliens/backend/global/config/MailConfig.java +++ b/src/main/java/com/aliens/backend/global/config/MailConfig.java @@ -30,10 +30,10 @@ public JavaMailSender javaMailService() { private Properties getMailProperties() { Properties prop = new Properties(); - prop.setProperty("mail.transport.protocol", "smtp"); - prop.setProperty("mail.smtp.auth", "true"); - prop.setProperty("mail.smtp.starttls.enable", "true"); - prop.setProperty("mail.debug", "true"); + prop.setProperty("mail.transport.protocol", emailProperties.getProtocol()); + prop.setProperty("mail.smtp.auth", emailProperties.getAuth()); + prop.setProperty("mail.smtp.starttls.enable", emailProperties.getStarttls()); + prop.setProperty("mail.debug",emailProperties.getDebug()); return prop; } } \ No newline at end of file diff --git a/src/main/java/com/aliens/backend/global/property/EmailProperties.java b/src/main/java/com/aliens/backend/global/property/EmailProperties.java index cb15afb3..949fc7f4 100644 --- a/src/main/java/com/aliens/backend/global/property/EmailProperties.java +++ b/src/main/java/com/aliens/backend/global/property/EmailProperties.java @@ -22,6 +22,34 @@ public class EmailProperties { @Value("${smtp.port}") private int port; + @Value("${smtp.protocol}") + private String protocol; + + @Value("${smtp.auth}") + private String auth; + + @Value("${smtp.starttls}") + private String starttls; + + @Value("${smtp.debug}") + private String debug; + + public String getProtocol() { + return protocol; + } + + public String getAuth() { + return auth; + } + + public String getDebug() { + return debug; + } + + public String getStarttls() { + return starttls; + } + public String getDomainUrl() { return domainUrl; }