From 979a4ac9f661af7ccc38edf5272d5b8fa94721a8 Mon Sep 17 00:00:00 2001 From: yumyeonghan Date: Mon, 23 Oct 2023 14:54:52 +0900 Subject: [PATCH 1/9] style: code formatting --- .../server/common/config/AuthWebConfig.java | 8 ++++++++ .../coffeemeet/server/common/util/FileUtils.java | 16 ++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/java/coffeemeet/server/common/config/AuthWebConfig.java b/src/main/java/coffeemeet/server/common/config/AuthWebConfig.java index 38715672..832bd391 100644 --- a/src/main/java/coffeemeet/server/common/config/AuthWebConfig.java +++ b/src/main/java/coffeemeet/server/common/config/AuthWebConfig.java @@ -2,9 +2,12 @@ import coffeemeet.server.auth.utils.JwtTokenProvider; import coffeemeet.server.auth.utils.converter.OAuthProviderConverter; +import coffeemeet.server.common.UserArgumentResolver; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; import org.springframework.format.FormatterRegistry; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration @@ -18,4 +21,9 @@ public void addFormatters(FormatterRegistry registry) { registry.addConverter(new OAuthProviderConverter()); } + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(new UserArgumentResolver(jwtTokenProvider)); + } + } diff --git a/src/main/java/coffeemeet/server/common/util/FileUtils.java b/src/main/java/coffeemeet/server/common/util/FileUtils.java index caf7f615..c0753eea 100644 --- a/src/main/java/coffeemeet/server/common/util/FileUtils.java +++ b/src/main/java/coffeemeet/server/common/util/FileUtils.java @@ -13,14 +13,6 @@ public final class FileUtils { private static final String MULTIPART_FILE_TRANSFER_ERROR = "MULTIPART FILE을 FILE로 변환 중 오류가 발생했습니다."; private static final String FILE_DELETE_ERROR = "FILE 삭제 중 오류가 발생했습니다."; - public static class FileIOException extends RuntimeException { - - public FileIOException(String message, Throwable e) { - super(message, e); - } - - } - public static File convertMultipartFileToFile(MultipartFile multipartFile) { try { File file = File.createTempFile("temp", multipartFile.getOriginalFilename()); @@ -39,4 +31,12 @@ public static void delete(File file) { } } + public static class FileIOException extends RuntimeException { + + public FileIOException(String message, Throwable e) { + super(message, e); + } + + } + } From c7d7e331d19a5901b22822eaef24ffbeaaf3d696 Mon Sep 17 00:00:00 2001 From: yumyeonghan Date: Mon, 23 Oct 2023 14:56:33 +0900 Subject: [PATCH 2/9] =?UTF-8?q?feat:=20AuthInfo=20=EB=A0=88=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로직에 사용될 userId와 토큰 검증에 사용될 refreshToken --- src/main/java/coffeemeet/server/user/dto/AuthInfo.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/coffeemeet/server/user/dto/AuthInfo.java diff --git a/src/main/java/coffeemeet/server/user/dto/AuthInfo.java b/src/main/java/coffeemeet/server/user/dto/AuthInfo.java new file mode 100644 index 00000000..1366c4d4 --- /dev/null +++ b/src/main/java/coffeemeet/server/user/dto/AuthInfo.java @@ -0,0 +1,4 @@ +package coffeemeet.server.user.dto; + +public record AuthInfo(Long userId, String refreshToken) { +} From 25415da8a6b44c0bb66b1879b5cc045ab38f62ea Mon Sep 17 00:00:00 2001 From: yumyeonghan Date: Mon, 23 Oct 2023 14:57:06 +0900 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20Login=20annotation=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 - 로그인 확인할 때 사용 됨 --- .../coffeemeet/server/common/annotation/Login.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/coffeemeet/server/common/annotation/Login.java diff --git a/src/main/java/coffeemeet/server/common/annotation/Login.java b/src/main/java/coffeemeet/server/common/annotation/Login.java new file mode 100644 index 00000000..7acf35b2 --- /dev/null +++ b/src/main/java/coffeemeet/server/common/annotation/Login.java @@ -0,0 +1,12 @@ +package coffeemeet.server.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface Login { + +} From f940cfcb18997092baab8f5f06a42f18c2eca22f Mon Sep 17 00:00:00 2001 From: yumyeonghan Date: Mon, 23 Oct 2023 14:59:45 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feat:=20UserArgumentResolver=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 - 헤더로 넘어오는 AccessToken을 통해 AuthInfo 객체로 데이터 변환 기능 --- .../server/common/UserArgumentResolver.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/main/java/coffeemeet/server/common/UserArgumentResolver.java diff --git a/src/main/java/coffeemeet/server/common/UserArgumentResolver.java b/src/main/java/coffeemeet/server/common/UserArgumentResolver.java new file mode 100644 index 00000000..c6a5eb96 --- /dev/null +++ b/src/main/java/coffeemeet/server/common/UserArgumentResolver.java @@ -0,0 +1,45 @@ +package coffeemeet.server.common; + +import coffeemeet.server.auth.utils.JwtTokenProvider; +import coffeemeet.server.common.annotation.Login; +import coffeemeet.server.user.dto.AuthInfo; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +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 +@RequiredArgsConstructor +public class UserArgumentResolver implements HandlerMethodArgumentResolver { + + private static final String AUTHENTICATION_FAILED_MESSAGE = "(%s)는 잘못된 권한 헤더입니다."; + + private final JwtTokenProvider jwtTokenProvider; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.getParameterType().equals(AuthInfo.class) && parameter.hasParameterAnnotation( + Login.class); + } + + @Override + public AuthInfo resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { + HttpServletRequest httpServletRequest = (HttpServletRequest) webRequest.getNativeRequest(); + String authHeader = httpServletRequest.getHeader("Authorization"); + + if (authHeader != null && authHeader.startsWith("Bearer ")) { + String token = authHeader.substring(7); + Long userId = jwtTokenProvider.extractUserId(token); + return new AuthInfo(userId, token); + } + throw new IllegalArgumentException( + String.format(AUTHENTICATION_FAILED_MESSAGE, authHeader) + ); + } + +} From 1a58b3381629d7a9d08704fe53c709dd0f1947c8 Mon Sep 17 00:00:00 2001 From: yumyeonghan Date: Mon, 23 Oct 2023 15:10:01 +0900 Subject: [PATCH 5/9] =?UTF-8?q?feat:=20CertificationController=20UserArgum?= =?UTF-8?q?entResolver=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CertificationController.java | 12 ++++++------ .../java/coffeemeet/server/user/dto/AuthInfo.java | 1 + 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/coffeemeet/server/certification/controller/CertificationController.java b/src/main/java/coffeemeet/server/certification/controller/CertificationController.java index 59eb9616..33544eb7 100644 --- a/src/main/java/coffeemeet/server/certification/controller/CertificationController.java +++ b/src/main/java/coffeemeet/server/certification/controller/CertificationController.java @@ -3,10 +3,11 @@ import static org.springframework.http.HttpStatus.CREATED; import coffeemeet.server.certification.service.CertificationService; +import coffeemeet.server.common.annotation.Login; import coffeemeet.server.common.util.FileUtils; +import coffeemeet.server.user.dto.AuthInfo; import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestPart; @@ -21,17 +22,16 @@ public class CertificationController { private final CertificationService certificationService; - @PostMapping("/users/{userId}/business-card") + @PostMapping("/users/business-card") @ResponseStatus(CREATED) public void uploadBusinessCard( - @PathVariable("userId") - @NotNull(message = "유저 ID는 null일 수 없습니다.") - long userId, + @Login + AuthInfo authInfo, @RequestPart("businessCard") @NotNull(message = "명함 이미지는 null일 수 없습니다.") MultipartFile businessCard ) { - certificationService.uploadBusinessCard(userId, + certificationService.uploadBusinessCard(authInfo.userId(), FileUtils.convertMultipartFileToFile(businessCard)); } diff --git a/src/main/java/coffeemeet/server/user/dto/AuthInfo.java b/src/main/java/coffeemeet/server/user/dto/AuthInfo.java index 1366c4d4..fac09343 100644 --- a/src/main/java/coffeemeet/server/user/dto/AuthInfo.java +++ b/src/main/java/coffeemeet/server/user/dto/AuthInfo.java @@ -1,4 +1,5 @@ package coffeemeet.server.user.dto; public record AuthInfo(Long userId, String refreshToken) { + } From d4edb827dd802e10c4cf166155febc55a4d3c135 Mon Sep 17 00:00:00 2001 From: yumyeonghan Date: Mon, 23 Oct 2023 16:06:16 +0900 Subject: [PATCH 6/9] =?UTF-8?q?feat:=20RefreshToken=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - key : userId, value : 토큰값 --- .../server/auth/RefreshTokenRepository.java | 8 +++++++ .../server/auth/domain/RefreshToken.java | 23 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/main/java/coffeemeet/server/auth/RefreshTokenRepository.java create mode 100644 src/main/java/coffeemeet/server/auth/domain/RefreshToken.java diff --git a/src/main/java/coffeemeet/server/auth/RefreshTokenRepository.java b/src/main/java/coffeemeet/server/auth/RefreshTokenRepository.java new file mode 100644 index 00000000..e9e53608 --- /dev/null +++ b/src/main/java/coffeemeet/server/auth/RefreshTokenRepository.java @@ -0,0 +1,8 @@ +package coffeemeet.server.auth; + +import coffeemeet.server.auth.domain.RefreshToken; +import org.springframework.data.repository.CrudRepository; + +public interface RefreshTokenRepository extends CrudRepository { + +} diff --git a/src/main/java/coffeemeet/server/auth/domain/RefreshToken.java b/src/main/java/coffeemeet/server/auth/domain/RefreshToken.java new file mode 100644 index 00000000..1b20301e --- /dev/null +++ b/src/main/java/coffeemeet/server/auth/domain/RefreshToken.java @@ -0,0 +1,23 @@ +package coffeemeet.server.auth.domain; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; + +@Getter +@RedisHash(value = "refresh", timeToLive = 1209600) +public class RefreshToken { + + @Id + private Long userId; + + private String value; + + @Builder + private RefreshToken(Long userId, String value) { + this.userId = userId; + this.value = value; + } + +} From 31830e0c17b710ad18a746af638ddcd1cfe0db62 Mon Sep 17 00:00:00 2001 From: yumyeonghan Date: Mon, 23 Oct 2023 16:07:14 +0900 Subject: [PATCH 7/9] =?UTF-8?q?feat:=20UserArgumentResolver=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 - refresh token 조회 로직 추가 --- .../server/common/UserArgumentResolver.java | 18 +++++++++++++++--- .../server/common/config/AuthWebConfig.java | 4 +++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/java/coffeemeet/server/common/UserArgumentResolver.java b/src/main/java/coffeemeet/server/common/UserArgumentResolver.java index c6a5eb96..ec5dedde 100644 --- a/src/main/java/coffeemeet/server/common/UserArgumentResolver.java +++ b/src/main/java/coffeemeet/server/common/UserArgumentResolver.java @@ -1,5 +1,7 @@ package coffeemeet.server.common; +import coffeemeet.server.auth.RefreshTokenRepository; +import coffeemeet.server.auth.domain.RefreshToken; import coffeemeet.server.auth.utils.JwtTokenProvider; import coffeemeet.server.common.annotation.Login; import coffeemeet.server.user.dto.AuthInfo; @@ -16,9 +18,12 @@ @RequiredArgsConstructor public class UserArgumentResolver implements HandlerMethodArgumentResolver { - private static final String AUTHENTICATION_FAILED_MESSAGE = "(%s)는 잘못된 권한 헤더입니다."; + private static final String HEADER_AUTHENTICATION_FAILED_MESSAGE = "(%s)는 잘못된 권한 헤더입니다."; + public static final String USER_AUTHENTICATION_FAILED_MESSAGE = "사용자(%s)의 갱신 토큰이 존재하지 않습니다."; private final JwtTokenProvider jwtTokenProvider; + private final RefreshTokenRepository refreshTokenRepository; + @Override public boolean supportsParameter(MethodParameter parameter) { @@ -35,11 +40,18 @@ public AuthInfo resolveArgument(MethodParameter parameter, ModelAndViewContainer if (authHeader != null && authHeader.startsWith("Bearer ")) { String token = authHeader.substring(7); Long userId = jwtTokenProvider.extractUserId(token); - return new AuthInfo(userId, token); + RefreshToken refreshToken = getRefreshToken(userId); + return new AuthInfo(userId, refreshToken.getValue()); } throw new IllegalArgumentException( - String.format(AUTHENTICATION_FAILED_MESSAGE, authHeader) + String.format(HEADER_AUTHENTICATION_FAILED_MESSAGE, authHeader) ); } + private RefreshToken getRefreshToken(Long userId) { + return refreshTokenRepository.findById(userId) + .orElseThrow(() -> new IllegalArgumentException(String.format( + USER_AUTHENTICATION_FAILED_MESSAGE, userId))); + } + } diff --git a/src/main/java/coffeemeet/server/common/config/AuthWebConfig.java b/src/main/java/coffeemeet/server/common/config/AuthWebConfig.java index 832bd391..43a234d2 100644 --- a/src/main/java/coffeemeet/server/common/config/AuthWebConfig.java +++ b/src/main/java/coffeemeet/server/common/config/AuthWebConfig.java @@ -1,5 +1,6 @@ package coffeemeet.server.common.config; +import coffeemeet.server.auth.RefreshTokenRepository; import coffeemeet.server.auth.utils.JwtTokenProvider; import coffeemeet.server.auth.utils.converter.OAuthProviderConverter; import coffeemeet.server.common.UserArgumentResolver; @@ -15,6 +16,7 @@ public class AuthWebConfig implements WebMvcConfigurer { private final JwtTokenProvider jwtTokenProvider; + private final RefreshTokenRepository refreshTokenRepository; @Override public void addFormatters(FormatterRegistry registry) { @@ -23,7 +25,7 @@ public void addFormatters(FormatterRegistry registry) { @Override public void addArgumentResolvers(List resolvers) { - resolvers.add(new UserArgumentResolver(jwtTokenProvider)); + resolvers.add(new UserArgumentResolver(jwtTokenProvider, refreshTokenRepository)); } } From 8e0f5adfac22779df4caa0df3451af35ec175241 Mon Sep 17 00:00:00 2001 From: yumyeonghan Date: Mon, 23 Oct 2023 16:07:46 +0900 Subject: [PATCH 8/9] style: code formatting --- .../java/coffeemeet/server/common/UserArgumentResolver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/coffeemeet/server/common/UserArgumentResolver.java b/src/main/java/coffeemeet/server/common/UserArgumentResolver.java index ec5dedde..3fb8b07e 100644 --- a/src/main/java/coffeemeet/server/common/UserArgumentResolver.java +++ b/src/main/java/coffeemeet/server/common/UserArgumentResolver.java @@ -18,8 +18,8 @@ @RequiredArgsConstructor public class UserArgumentResolver implements HandlerMethodArgumentResolver { - private static final String HEADER_AUTHENTICATION_FAILED_MESSAGE = "(%s)는 잘못된 권한 헤더입니다."; public static final String USER_AUTHENTICATION_FAILED_MESSAGE = "사용자(%s)의 갱신 토큰이 존재하지 않습니다."; + private static final String HEADER_AUTHENTICATION_FAILED_MESSAGE = "(%s)는 잘못된 권한 헤더입니다."; private final JwtTokenProvider jwtTokenProvider; private final RefreshTokenRepository refreshTokenRepository; From f141049267364ba5ceeda458917fd28e34af7609 Mon Sep 17 00:00:00 2001 From: yumyeonghan Date: Mon, 23 Oct 2023 16:10:48 +0900 Subject: [PATCH 9/9] style: code formatting --- .../coffeemeet/server/common/config/ControllerTestConfig.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/java/coffeemeet/server/common/config/ControllerTestConfig.java b/src/test/java/coffeemeet/server/common/config/ControllerTestConfig.java index f9f30ee2..32e9ec5b 100644 --- a/src/test/java/coffeemeet/server/common/config/ControllerTestConfig.java +++ b/src/test/java/coffeemeet/server/common/config/ControllerTestConfig.java @@ -3,6 +3,7 @@ import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import coffeemeet.server.auth.RefreshTokenRepository; import coffeemeet.server.auth.utils.JwtTokenProvider; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; @@ -27,6 +28,9 @@ public abstract class ControllerTestConfig { @MockBean protected JwtTokenProvider jwtTokenProvider; + @MockBean + protected RefreshTokenRepository refreshTokenRepository; + @BeforeEach void setUp(WebApplicationContext ctx, RestDocumentationContextProvider restDocumentation) { mockMvc = MockMvcBuilders.webAppContextSetup(ctx)