From 2c99aaa215306cf87f01eecc264ed7ae5aff8ba7 Mon Sep 17 00:00:00 2001 From: jbh010204 Date: Mon, 11 Aug 2025 06:25:04 +0900 Subject: [PATCH 1/6] =?UTF-8?q?refactor:=20s3key=20=EA=B8=B8=EC=9D=B4=20?= =?UTF-8?q?=EC=B5=9C=EB=8C=80=20255=20=EC=9E=90=20=EB=84=98=EC=A7=80=20?= =?UTF-8?q?=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 --- .../infra/persistence/s3/S3Service.java | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/main/java/life/mosu/mosuserver/infra/persistence/s3/S3Service.java b/src/main/java/life/mosu/mosuserver/infra/persistence/s3/S3Service.java index 823f978b..1abdec88 100644 --- a/src/main/java/life/mosu/mosuserver/infra/persistence/s3/S3Service.java +++ b/src/main/java/life/mosu/mosuserver/infra/persistence/s3/S3Service.java @@ -31,13 +31,23 @@ @RequiredArgsConstructor public class S3Service { + private static final int MAX_FILENAME_LENGTH = 150; + private static final int MAX_S3_KEY_LENGTH = 255; + private final S3Client s3Client; private final S3Presigner s3Presigner; private final S3Properties s3Properties; public FileUploadResponse uploadFile(MultipartFile file, Folder folder) { String sanitizedName = sanitizeFileName(file.getOriginalFilename()); - String s3Key = folder.getPath() + "/" + UUID.randomUUID() + "_" + sanitizedName; + String randomPrefix = UUID.randomUUID().toString(); + String s3Key = folder.getPath() + "/" + randomPrefix + "_" + sanitizedName; + + if (s3Key.length() > MAX_S3_KEY_LENGTH) { + int excess = s3Key.length() - MAX_S3_KEY_LENGTH; + sanitizedName = sanitizedName.substring(0, sanitizedName.length() - excess); + s3Key = folder.getPath() + "/" + randomPrefix + "_" + sanitizedName; + } try { s3Client.putObject( @@ -108,12 +118,23 @@ public String getPreSignedUrl(String s3Key) { } private String sanitizeFileName(String originalFilename) { - try { - return URLEncoder.encode(originalFilename, StandardCharsets.UTF_8) - .replaceAll("\\+", "%20"); - } catch (Exception e) { - throw new RuntimeException("파일 이름 인코딩 실패", e); + + String encoded = URLEncoder.encode(originalFilename, StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"); + + // 파일명만 잘라내기 (확장자 유지) + String extension = ""; + int dotIndex = encoded.lastIndexOf('.'); + if (dotIndex != -1) { + extension = encoded.substring(dotIndex); + encoded = encoded.substring(0, dotIndex); + } + + if (encoded.length() > MAX_FILENAME_LENGTH) { + encoded = encoded.substring(0, MAX_FILENAME_LENGTH); } + + return encoded; } private String shortenKey(String key) { From d0157b1df12737dd83873a1fda28972fd35005e3 Mon Sep 17 00:00:00 2001 From: jbh010204 Date: Mon, 11 Aug 2025 06:40:06 +0900 Subject: [PATCH 2/6] =?UTF-8?q?refactor:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EC=8B=9C=20=ED=8C=8C=EC=9D=BC=20temp=20->?= =?UTF-8?q?=20update=EB=A1=9C=20tag=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../life/mosu/mosuserver/application/event/EventService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/life/mosu/mosuserver/application/event/EventService.java b/src/main/java/life/mosu/mosuserver/application/event/EventService.java index 0fb020ed..7cf4bf8a 100644 --- a/src/main/java/life/mosu/mosuserver/application/event/EventService.java +++ b/src/main/java/life/mosu/mosuserver/application/event/EventService.java @@ -7,6 +7,7 @@ import life.mosu.mosuserver.global.exception.CustomRuntimeException; import life.mosu.mosuserver.global.exception.ErrorCode; import life.mosu.mosuserver.global.support.CursorResponse; +import life.mosu.mosuserver.infra.persistence.s3.FileUploadHelper; import life.mosu.mosuserver.infra.persistence.s3.S3Service; import life.mosu.mosuserver.presentation.event.dto.EventRequest; import life.mosu.mosuserver.presentation.event.dto.EventResponse; @@ -22,12 +23,13 @@ public class EventService { private final EventJpaRepository eventJpaRepository; private final EventQueryRepository eventQueryRepository; - // private final EventAttachmentService attachmentService; + private final FileUploadHelper uploadHelper; private final S3Service s3Service; @Transactional public void createEvent(EventRequest request) { EventJpaEntity eventEntity = eventJpaRepository.save(request.toEntity()); + uploadHelper.updateTag(eventEntity.getS3Key()); // attachmentService.createAttachment(request.optionalAttachment(), eventEntity); } From 05927d7694eb6045b98936c38e40d9ee9baac1d1 Mon Sep 17 00:00:00 2001 From: wlgns12370 Date: Mon, 11 Aug 2025 06:50:58 +0900 Subject: [PATCH 3/6] =?UTF-8?q?MOSU=20refactor:=20update=20lunch=20option?= =?UTF-8?q?=20text=20to=20"=EB=8F=84=EC=8B=9C=EB=9D=BD=20X"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/examapplication/ExamApplicationService.java | 2 +- .../life/mosu/mosuserver/domain/application/entity/Lunch.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/life/mosu/mosuserver/application/examapplication/ExamApplicationService.java b/src/main/java/life/mosu/mosuserver/application/examapplication/ExamApplicationService.java index 8fd8d1ef..bde22bd6 100644 --- a/src/main/java/life/mosu/mosuserver/application/examapplication/ExamApplicationService.java +++ b/src/main/java/life/mosu/mosuserver/application/examapplication/ExamApplicationService.java @@ -153,7 +153,7 @@ public ExamApplicationInfoResponse getApplication(Long userId, Long examApplicat examApplicationInfo.schoolName(), AddressResponse.from(examApplicationInfo.address()), subjects, - examApplicationInfo.isLunchChecked() ? examApplicationInfo.lunchName() : "신청 안 함", + examApplicationInfo.isLunchChecked() ? examApplicationInfo.lunchName() : "도시락 X", paymentAmount, discountAmount, examApplicationInfo.paymentMethod().getName() diff --git a/src/main/java/life/mosu/mosuserver/domain/application/entity/Lunch.java b/src/main/java/life/mosu/mosuserver/domain/application/entity/Lunch.java index 835f24e6..2ff07bba 100644 --- a/src/main/java/life/mosu/mosuserver/domain/application/entity/Lunch.java +++ b/src/main/java/life/mosu/mosuserver/domain/application/entity/Lunch.java @@ -9,7 +9,7 @@ @Getter @RequiredArgsConstructor public enum Lunch { - NONE("선택 안 함"), + NONE("도시락 X"), OPTION1("도시락 A"), OPTION2("도시락 B"), OPTION3("비건 도시락"), From 25ae7f0618d0d07b19c33fed437be19508afe08e Mon Sep 17 00:00:00 2001 From: jbh010204 Date: Mon, 11 Aug 2025 06:58:49 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20=EC=BF=A0=ED=82=A4=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20=EC=A0=9C=EC=95=BD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/life/mosu/mosuserver/global/filter/AuthConstants.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/life/mosu/mosuserver/global/filter/AuthConstants.java b/src/main/java/life/mosu/mosuserver/global/filter/AuthConstants.java index a2e36e42..a884a49e 100644 --- a/src/main/java/life/mosu/mosuserver/global/filter/AuthConstants.java +++ b/src/main/java/life/mosu/mosuserver/global/filter/AuthConstants.java @@ -18,6 +18,7 @@ public class AuthConstants { public static final String AUTH_PREFIX = API_PREFIX + "/auth"; public static final String PATH_REISSUE = AUTH_PREFIX + "/reissue"; public static final String PATH_SIGNUP = AUTH_PREFIX + "/signup"; + public static final String COOKIE_ACCESS = AUTH_PREFIX + "/check-cookie"; public static final String USER_PREFIX = API_PREFIX + "/user"; public static final String PATH_PASSWORD_CHANGE = USER_PREFIX + "/me/password"; From c0576ab2be864de6da7ddc9e0647b59b242300ed Mon Sep 17 00:00:00 2001 From: jbh010204 Date: Mon, 11 Aug 2025 06:59:00 +0900 Subject: [PATCH 5/6] =?UTF-8?q?feat:=20develop=20=EC=BF=A0=ED=82=A4=20secu?= =?UTF-8?q?re=20false=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../life/mosu/mosuserver/global/util/CookieBuilderUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/life/mosu/mosuserver/global/util/CookieBuilderUtil.java b/src/main/java/life/mosu/mosuserver/global/util/CookieBuilderUtil.java index 7f5ea9aa..3949908e 100644 --- a/src/main/java/life/mosu/mosuserver/global/util/CookieBuilderUtil.java +++ b/src/main/java/life/mosu/mosuserver/global/util/CookieBuilderUtil.java @@ -81,7 +81,7 @@ public static ResponseCookie createDevelopResponseCookie(String name, String val */ public static Cookie createDevelopCookie(String name, String value, Long maxAge) { Cookie cookie = createBaseServletCookie(name, value, maxAge); - cookie.setSecure(true); + cookie.setSecure(false); cookie.setDomain(".mosuedu.com"); return cookie; } From 5264184fce501177c122280f3be5024c573c2722 Mon Sep 17 00:00:00 2001 From: jbh010204 Date: Mon, 11 Aug 2025 06:59:13 +0900 Subject: [PATCH 6/6] =?UTF-8?q?feat:=20=EC=BF=A0=ED=82=A4=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/exception/ErrorCode.java | 1 + .../mosuserver/global/filter/TokenFilter.java | 20 +++++++++++++++++++ .../presentation/auth/AuthController.java | 12 +++++++++-- .../presentation/auth/AuthControllerDocs.java | 3 +++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java b/src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java index e9918494..12499a2a 100644 --- a/src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java +++ b/src/main/java/life/mosu/mosuserver/global/exception/ErrorCode.java @@ -32,6 +32,7 @@ public enum ErrorCode { INVALID_SIGN_UP_TOKEN(HttpStatus.UNAUTHORIZED, "유효하지 않은 회원가입 인증 토큰입니다."), MISSING_SIGNUP_TOKEN(HttpStatus.BAD_REQUEST, "회원가입 인증 토큰이 누락되었습니다."), MISSING_PASSWORD_TOKEN(HttpStatus.BAD_REQUEST, "비밀번호 변경 토큰이 누락되었습니다."), + COOKIE_NOT_FOUND(HttpStatus.NOT_FOUND, "쿠키가 존재하지 않습니다."), NOT_FOUND_TOKEN(HttpStatus.NOT_FOUND, "인증 토큰을 찾을 수 없습니다."), NOT_FOUND_ACCESS_TOKEN(HttpStatus.UNAUTHORIZED, "액세스 토큰을 찾을 수 없습니다."), diff --git a/src/main/java/life/mosu/mosuserver/global/filter/TokenFilter.java b/src/main/java/life/mosu/mosuserver/global/filter/TokenFilter.java index 6a109baf..1ff63eb9 100644 --- a/src/main/java/life/mosu/mosuserver/global/filter/TokenFilter.java +++ b/src/main/java/life/mosu/mosuserver/global/filter/TokenFilter.java @@ -78,6 +78,26 @@ protected void doFilterInternal( return; } + if (requestUri.startsWith(AuthConstants.COOKIE_ACCESS)) { + + final TokenCookies tokenCookies = tokenResolver.resolveTokens(request); + String accessToken = tokenCookies.getAccessToken().orElseThrow( + () -> new CustomRuntimeException(ErrorCode.COOKIE_NOT_FOUND) + ); + try { + setAuthentication(accessToken); + + response.setStatus(HttpServletResponse.SC_NO_CONTENT); + return; + } catch (CustomRuntimeException e) { + log.warn("쿠키 토큰 검증 실패: {}", e.getMessage()); + throw e; + } catch (Exception e) { + log.error("쿠키 토큰 검증 중 예외 발생", e); + throw new CustomRuntimeException(ErrorCode.INVALID_TOKEN); + } + } + final TokenCookies tokenCookies = tokenResolver.resolveTokens(request); String accessToken = tokenCookies.getAccessToken().orElseThrow( () -> new CustomRuntimeException(ErrorCode.NOT_FOUND_ACCESS_TOKEN) diff --git a/src/main/java/life/mosu/mosuserver/presentation/auth/AuthController.java b/src/main/java/life/mosu/mosuserver/presentation/auth/AuthController.java index a75acc3a..e517ecb5 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/auth/AuthController.java +++ b/src/main/java/life/mosu/mosuserver/presentation/auth/AuthController.java @@ -12,6 +12,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -42,15 +43,22 @@ public ResponseEntity> login( )); } + @GetMapping("/check-cookie") + public ResponseEntity checkToken() { + return ResponseEntity.ok().build(); + } + + ; + private HttpHeaders applyTokenHeader(Token token) { HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.SET_COOKIE, CookieBuilderUtil.createDevelopCookieString( + headers.add(HttpHeaders.SET_COOKIE, CookieBuilderUtil.createLocalCookieString( CookieBuilderUtil.ACCESS_TOKEN_COOKIE_NAME, token.accessToken(), token.accessTokenExpireTime() )); - headers.add(HttpHeaders.SET_COOKIE, CookieBuilderUtil.createDevelopCookieString( + headers.add(HttpHeaders.SET_COOKIE, CookieBuilderUtil.createLocalCookieString( CookieBuilderUtil.REFRESH_TOKEN_COOKIE_NAME, token.refreshToken(), token.refreshTokenExpireTime() diff --git a/src/main/java/life/mosu/mosuserver/presentation/auth/AuthControllerDocs.java b/src/main/java/life/mosu/mosuserver/presentation/auth/AuthControllerDocs.java index 59024185..88411980 100644 --- a/src/main/java/life/mosu/mosuserver/presentation/auth/AuthControllerDocs.java +++ b/src/main/java/life/mosu/mosuserver/presentation/auth/AuthControllerDocs.java @@ -15,4 +15,7 @@ public interface AuthControllerDocs { @Operation(description = "로그인 API 지금은 쿠키와 response 둘다 반환하는데 곧 쿠키로만 작동하게 할 것 입니다. <프론트하고 변경하려고 Response 이렇게 만들었는데 나중에 같이 맞춥시다!>", summary = "사용자가 로그인합니다.") public ResponseEntity> login( @RequestBody @Valid final LoginRequest request); + + @Operation(description = "쿠키 검증용 API", summary = "쿠키가 유효한지 확인합니다.") + public ResponseEntity checkToken(); }