From a4388032d85358cf32559266ea3513556e39c65a Mon Sep 17 00:00:00 2001 From: SinnoLn Date: Mon, 26 May 2025 17:41:10 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat(auth):=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EC=8B=9C=20isTempPassword=20=EC=97=AC=EB=B6=80=20JSON=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=EC=97=90=20=ED=8F=AC=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LoginFilter에서 로그인 성공 시 JSON 응답에 isTempPassword 필드 추가 - CustomUserDetails에 isTempPassword() 메서드 추가 - LoginResponse 클래스 정의로 Swagger 명세 유지 - 임시 비밀번호 사용자는 프론트에서 비밀번호 변경 유도 가능 --- .../auth/adapter/web/AuthApiDocument.java | 15 ++++++---- .../dto/dto/CustomUserDetails.java | 4 +++ .../dto/response/LoginResponse.java | 11 +++++++ .../backend/shared/security/LoginFilter.java | 30 +++++++++++++++++-- 4 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 src/main/java/org/nova/backend/auth/application/dto/response/LoginResponse.java diff --git a/src/main/java/org/nova/backend/auth/adapter/web/AuthApiDocument.java b/src/main/java/org/nova/backend/auth/adapter/web/AuthApiDocument.java index de2cc88b..874cee06 100644 --- a/src/main/java/org/nova/backend/auth/adapter/web/AuthApiDocument.java +++ b/src/main/java/org/nova/backend/auth/adapter/web/AuthApiDocument.java @@ -3,6 +3,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; @@ -10,6 +11,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.nova.backend.auth.application.dto.response.LoginResponse; @Tag(name = "회원 인증 API", description = "회원가입 로그인 API") public @interface AuthApiDocument { @@ -56,15 +58,19 @@ @Operation(summary = "로그인", description = "회원가입된 정보로 본인 인증 로그인") @ApiResponses({ - @ApiResponse(responseCode = "200", description = "로그인 성공"), + @ApiResponse( + responseCode = "200", + description = "로그인 성공", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = LoginResponse.class)) + ), @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터"), @ApiResponse(responseCode = "401", description = "로그인 실패. 학번 또는 비밀번호를 다시 확인해주세요."), @ApiResponse(responseCode = "500", description = "서버 오류") }) @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) - @interface LoginApiDoc { - } + @interface LoginApiDoc {} @Operation(summary = "로그아웃", description = "로그인한 정보를 삭제합니다. AUTH_TOKEN 담긴 쿠키 삭제") @ApiResponses({ @@ -99,8 +105,7 @@ ✔️ 발급된 임시 비밀번호는 10자리 랜덤 문자열이며, 기존 비밀번호를 대체하여 저장됩니다. ✔️ 사용자의 비밀번호는 암호화되어 저장되며, `isTempPassword` 플래그가 true로 설정됩니다. ✔️ 로그인 시 `isTempPassword`가 true인 사용자는 비밀번호 변경 페이지로 리다이렉트해야 합니다. - """, - tags = {"Password Reset API"} + """ ) @ApiResponses({ @ApiResponse(responseCode = "200", description = "임시 비밀번호 이메일 전송 성공"), diff --git a/src/main/java/org/nova/backend/auth/application/dto/dto/CustomUserDetails.java b/src/main/java/org/nova/backend/auth/application/dto/dto/CustomUserDetails.java index 052c3822..d077cd1c 100644 --- a/src/main/java/org/nova/backend/auth/application/dto/dto/CustomUserDetails.java +++ b/src/main/java/org/nova/backend/auth/application/dto/dto/CustomUserDetails.java @@ -46,4 +46,8 @@ public String getStudentNumber(){ public Member getMember() { return member; } + + public boolean isTempPassword() { + return member.isTempPassword(); + } } diff --git a/src/main/java/org/nova/backend/auth/application/dto/response/LoginResponse.java b/src/main/java/org/nova/backend/auth/application/dto/response/LoginResponse.java new file mode 100644 index 00000000..f99994cc --- /dev/null +++ b/src/main/java/org/nova/backend/auth/application/dto/response/LoginResponse.java @@ -0,0 +1,11 @@ +package org.nova.backend.auth.application.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class LoginResponse { + private String message; + private boolean isTempPassword; +} \ No newline at end of file diff --git a/src/main/java/org/nova/backend/shared/security/LoginFilter.java b/src/main/java/org/nova/backend/shared/security/LoginFilter.java index c3440c97..6c036a03 100644 --- a/src/main/java/org/nova/backend/shared/security/LoginFilter.java +++ b/src/main/java/org/nova/backend/shared/security/LoginFilter.java @@ -67,13 +67,35 @@ private AuthLoginDTO getLoginDataFromHTTPRequest(HttpServletRequest request) { @Override - protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, - Authentication authentication) { + protected void successfulAuthentication( + HttpServletRequest request, + HttpServletResponse response, + FilterChain chain, + Authentication authentication + ) throws IOException { log.info("로그인 성공"); + CustomUserDetails customUserDetails = (CustomUserDetails) authentication.getPrincipal(); String token = createAuthorizationToken(authentication); - //쿠키에 auth 토큰 담기 + // JSON 응답 추가 + response.setContentType("application/json"); + response.setCharacterEncoding("UTF-8"); + + boolean isTempPassword = customUserDetails.isTempPassword(); + + String jsonResponse = String.format(""" + { + "status": 200, + "message": "success", + "data": { + "message": "로그인 성공", + "tempPassword": %s + } + } + """, isTempPassword); + + //헤더 설정 Cookie cookie = new Cookie("AUTH_TOKEN", token); cookie.setHttpOnly(true); cookie.setPath("/"); @@ -84,6 +106,8 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR token, 60 * 60 * 5, isSecureCookie ? "; Secure" : "" )); + + response.getWriter().write(jsonResponse); } /* From 43930bd08f4f0e15cee5bb9fcd8ed345d8d0b8d2 Mon Sep 17 00:00:00 2001 From: SinnoLn Date: Mon, 26 May 2025 18:01:49 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=EB=B9=84=ED=9A=8C=EC=9B=90=20?= =?UTF-8?q?=EC=A0=91=EA=B7=BC=20=EC=A0=9C=ED=95=9C=20(403=20Forbidden=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/member/application/service/MemberService.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/nova/backend/member/application/service/MemberService.java b/src/main/java/org/nova/backend/member/application/service/MemberService.java index 847a0b94..aa43a2b9 100644 --- a/src/main/java/org/nova/backend/member/application/service/MemberService.java +++ b/src/main/java/org/nova/backend/member/application/service/MemberService.java @@ -192,6 +192,11 @@ public MemberSimpleProfileResponse getSimpleProfile(UUID memberId) { public MyPageMemberResponse getMemberProfile(UUID profileMemberId, UUID loginMemberId) { Member profileMember = findByMemberId(profileMemberId); boolean isLoginMember = isLoginMember(profileMemberId, loginMemberId); + + if (!isLoginMember && profileMember.getStudentNumber().equals(adminStudentNumber)) { + throw new MemberDomainException("관리자 프로필은 조회할 수 없습니다.", HttpStatus.FORBIDDEN); + } + if (!isLoginMember) { findByMemberId(loginMemberId); }