From 8219d0bd17a278c5d441aa745726c06bb60b19b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B5=AC=ED=99=98=EC=A4=80/=EB=AA=A8=EA=B1=B4?= Date: Mon, 8 Apr 2024 22:34:03 +0900 Subject: [PATCH] =?UTF-8?q?[REFACTOR]/#86=20-=20Exception=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 +- .../comment/service/CommentService.java | 6 +- .../domain/comment/service/ReplyService.java | 6 +- .../domain/member/service/MemberService.java | 4 +- .../tree/domain/post/service/PostService.java | 6 +- .../domain/tree/service/TreeQueryService.java | 2 +- .../tree/global/common/ApiResponse.java | 4 +- .../tree/global/exception/AuthErrorCode.java | 49 +++++++++++++ .../tree/global/exception/AuthException.java | 8 +++ .../tree/global/exception/BaseErrorCode.java | 8 +++ .../tree/global/exception/ErrorReasonDTO.java | 16 +++++ .../global/exception/GeneralException.java | 11 ++- .../exception/GeneralExceptionHandler.java | 19 ++--- .../global/exception/GlobalErrorCode.java | 71 +++++++++++-------- .../exception/JwtAuthenticationException.java | 2 +- .../global/security/filter/JwtAuthFilter.java | 10 ++- .../handler/JwtAccessDeniedHandler.java | 6 +- .../handler/JwtAuthenticationEntryPoint.java | 6 +- .../JwtAuthenticationExceptionHandler.java | 4 +- .../security/provider/TokenProvider.java | 16 ++--- src/main/resources/application.yml | 4 +- 21 files changed, 195 insertions(+), 68 deletions(-) create mode 100644 src/main/java/org/example/tree/global/exception/AuthErrorCode.java create mode 100644 src/main/java/org/example/tree/global/exception/AuthException.java create mode 100644 src/main/java/org/example/tree/global/exception/BaseErrorCode.java create mode 100644 src/main/java/org/example/tree/global/exception/ErrorReasonDTO.java diff --git a/build.gradle b/build.gradle index c9b1776..1d593f5 100644 --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ dependencies { runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' - // reids + // redis implementation 'org.springframework.boot:spring-boot-starter-data-redis:2.3.1.RELEASE' // lombok @@ -56,6 +56,9 @@ dependencies { // jackson implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.8' + // validation + implementation 'org.springframework.boot:spring-boot-starter-validation' + runtimeOnly 'com.h2database:h2' testImplementation 'org.springframework.boot:spring-boot-starter-test' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" diff --git a/src/main/java/org/example/tree/domain/comment/service/CommentService.java b/src/main/java/org/example/tree/domain/comment/service/CommentService.java index c6cfb3e..1a62891 100644 --- a/src/main/java/org/example/tree/domain/comment/service/CommentService.java +++ b/src/main/java/org/example/tree/domain/comment/service/CommentService.java @@ -16,6 +16,8 @@ import org.example.tree.domain.profile.service.ProfileService; import org.example.tree.domain.reaction.dto.ReactionResponseDTO; import org.example.tree.domain.reaction.service.ReactionService; +import org.example.tree.global.exception.AuthErrorCode; +import org.example.tree.global.exception.AuthException; import org.example.tree.global.exception.GeneralException; import org.example.tree.global.exception.GlobalErrorCode; import org.springframework.stereotype.Component; @@ -66,7 +68,7 @@ public void updateComment(Long treeId, Long postId, Long commentId, CommentReque Post post = postQueryService.findById(postId); Comment comment = commentQueryService.findById(commentId); if (!comment.getProfile().getId().equals(profile.getId())) { - throw new GeneralException(GlobalErrorCode.AUTHENTICATION_REQUIRED); + throw new AuthException(AuthErrorCode.AUTHENTICATION_REQUIRED); } comment.updateComment(request.getContent()); } @@ -77,7 +79,7 @@ public void deleteComment(Long treeId, Long postId, Long commentId, Member membe Post post = postQueryService.findById(postId); Comment comment = commentQueryService.findById(commentId); if (!comment.getProfile().getId().equals(profile.getId())) { - throw new GeneralException(GlobalErrorCode.AUTHENTICATION_REQUIRED); + throw new AuthException(AuthErrorCode.AUTHENTICATION_REQUIRED); } post.decreaseCommentCount(); commentCommandService.deleteComment(comment); diff --git a/src/main/java/org/example/tree/domain/comment/service/ReplyService.java b/src/main/java/org/example/tree/domain/comment/service/ReplyService.java index baa0024..f849b04 100644 --- a/src/main/java/org/example/tree/domain/comment/service/ReplyService.java +++ b/src/main/java/org/example/tree/domain/comment/service/ReplyService.java @@ -12,6 +12,8 @@ import org.example.tree.domain.profile.service.ProfileService; import org.example.tree.domain.reaction.dto.ReactionResponseDTO; import org.example.tree.domain.reaction.service.ReactionService; +import org.example.tree.global.exception.AuthErrorCode; +import org.example.tree.global.exception.AuthException; import org.example.tree.global.exception.GeneralException; import org.example.tree.global.exception.GlobalErrorCode; import org.springframework.stereotype.Component; @@ -54,7 +56,7 @@ public void updateReply(Long treeId, Long commentId, Long replyId, ReplyRequestD Comment comment = commentQueryService.findById(commentId); Reply reply = replyQueryService.findById(replyId); if (!reply.getProfile().getId().equals(profile.getId())) { - throw new GeneralException(GlobalErrorCode.AUTHENTICATION_REQUIRED); + throw new AuthException(AuthErrorCode.AUTHENTICATION_REQUIRED); } reply.updateReply(request.getContent()); } @@ -65,7 +67,7 @@ public void deleteReply(Long treeId, Long commentId, Long replyId, Member member Comment comment = commentQueryService.findById(commentId); Reply reply = replyQueryService.findById(replyId); if (!reply.getProfile().getId().equals(profile.getId())) { - throw new GeneralException(GlobalErrorCode.AUTHENTICATION_REQUIRED); + throw new AuthException(AuthErrorCode.AUTHENTICATION_REQUIRED); } replyCommandService.deleteReply(reply); } diff --git a/src/main/java/org/example/tree/domain/member/service/MemberService.java b/src/main/java/org/example/tree/domain/member/service/MemberService.java index 2f2fd9e..705534a 100644 --- a/src/main/java/org/example/tree/domain/member/service/MemberService.java +++ b/src/main/java/org/example/tree/domain/member/service/MemberService.java @@ -7,6 +7,8 @@ import org.example.tree.domain.member.dto.MemberResponseDTO; import org.example.tree.domain.member.entity.Member; import org.example.tree.domain.member.entity.redis.RefreshToken; +import org.example.tree.global.exception.AuthErrorCode; +import org.example.tree.global.exception.AuthException; import org.example.tree.global.exception.GeneralException; import org.example.tree.global.exception.GlobalErrorCode; import org.example.tree.global.redis.service.RedisService; @@ -47,7 +49,7 @@ public MemberResponseDTO.registerMember login(MemberRequestDTO.loginMember reque @Transactional public MemberResponseDTO.reissue reissue(MemberRequestDTO.reissue request) { - RefreshToken refreshToken = redisService.findRefreshToken(request.getRefreshToken()).orElseThrow(() -> new GeneralException(GlobalErrorCode.REFRESH_TOKEN_EXPIRED)); + RefreshToken refreshToken = redisService.findRefreshToken(request.getRefreshToken()).orElseThrow(() -> new AuthException(AuthErrorCode.REFRESH_TOKEN_EXPIRED)); Member member = memberQueryService.findById(refreshToken.getMemberId()); TokenDTO token = memberCommandService.reissueToken(member,refreshToken); return memberConverter.toReissue(token.getAccessToken(), token.getRefreshToken()); diff --git a/src/main/java/org/example/tree/domain/post/service/PostService.java b/src/main/java/org/example/tree/domain/post/service/PostService.java index 99f0fa2..b8907c1 100644 --- a/src/main/java/org/example/tree/domain/post/service/PostService.java +++ b/src/main/java/org/example/tree/domain/post/service/PostService.java @@ -15,6 +15,8 @@ import org.example.tree.domain.profile.service.ProfileService; import org.example.tree.domain.reaction.dto.ReactionResponseDTO; import org.example.tree.domain.reaction.service.ReactionService; +import org.example.tree.global.exception.AuthErrorCode; +import org.example.tree.global.exception.AuthException; import org.example.tree.global.exception.GeneralException; import org.example.tree.global.exception.GlobalErrorCode; import org.springframework.stereotype.Component; @@ -93,7 +95,7 @@ public void updatePost(Long treeId, Long postId, PostRequestDTO.updatePost reque Profile profile = profileService.getTreeProfile(member, treeId); Post post = postQueryService.findById(postId); if (!post.getProfile().getId().equals(profile.getId())) { - throw new GeneralException(GlobalErrorCode.AUTHENTICATION_REQUIRED); + throw new AuthException(AuthErrorCode.AUTHENTICATION_REQUIRED); } post.updatePost(request.getContent()); } @@ -103,7 +105,7 @@ public void deletePost(Long treeId, Long postId, Member member) { Profile profile = profileService.getTreeProfile(member, treeId); Post post = postQueryService.findById(postId); if (!post.getProfile().getId().equals(profile.getId())) { - throw new GeneralException(GlobalErrorCode.AUTHENTICATION_REQUIRED); + throw new AuthException(AuthErrorCode.AUTHENTICATION_REQUIRED); } postCommandService.deletePost(post); } diff --git a/src/main/java/org/example/tree/domain/tree/service/TreeQueryService.java b/src/main/java/org/example/tree/domain/tree/service/TreeQueryService.java index fb4cfc3..eed46b2 100644 --- a/src/main/java/org/example/tree/domain/tree/service/TreeQueryService.java +++ b/src/main/java/org/example/tree/domain/tree/service/TreeQueryService.java @@ -17,7 +17,7 @@ public class TreeQueryService { public Tree findById(Long id) { return treeRepository.findById(id) - .orElseThrow(()->new GeneralException(GlobalErrorCode.TREE_NOT_FOUND)); + .orElseThrow(()->new GeneralException(GlobalErrorCode.TREEHOUSE_NOT_FOUND)); } } diff --git a/src/main/java/org/example/tree/global/common/ApiResponse.java b/src/main/java/org/example/tree/global/common/ApiResponse.java index 89f9cb1..caf7ef8 100644 --- a/src/main/java/org/example/tree/global/common/ApiResponse.java +++ b/src/main/java/org/example/tree/global/common/ApiResponse.java @@ -53,8 +53,8 @@ public static ApiResponse onSuccess(T data){ } // 실패한 경우 응답 생성 - public static ApiResponse onFailure(GlobalErrorCode code, T data){ - return new ApiResponse<>(false, String.valueOf(code.getHttpStatus().value()), code.getMessage(), LocalDateTime.now(), data); + public static ApiResponse onFailure(String code, String message, T data){ + return new ApiResponse<>(false,code, message, LocalDateTime.now(), data); } } diff --git a/src/main/java/org/example/tree/global/exception/AuthErrorCode.java b/src/main/java/org/example/tree/global/exception/AuthErrorCode.java new file mode 100644 index 0000000..3366ceb --- /dev/null +++ b/src/main/java/org/example/tree/global/exception/AuthErrorCode.java @@ -0,0 +1,49 @@ +package org.example.tree.global.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import static org.springframework.http.HttpStatus.*; + +@Getter +@AllArgsConstructor +public enum AuthErrorCode implements BaseErrorCode{ + // 401 Unauthorized - 권한 없음 + TOKEN_EXPIRED(UNAUTHORIZED, "AUTH401_1", "인증 토큰이 만료 되었습니다. 토큰을 재발급 해주세요"), + INVALID_TOKEN(UNAUTHORIZED, "AUTH401_2", "인증 토큰이 유효하지 않습니다."), + INVALID_REFRESH_TOKEN(UNAUTHORIZED, "AUTH401_3", "리프레시 토큰이 유효하지 않습니다."), + REFRESH_TOKEN_EXPIRED(UNAUTHORIZED, "AUTH401_4", "리프레시 토큰이 만료 되었습니다."), + AUTHENTICATION_REQUIRED(UNAUTHORIZED, "AUTH401_5", "인증 정보가 유효하지 않습니다."), + LOGIN_REQUIRED(UNAUTHORIZED, "AUTH401_6", "로그인이 필요한 서비스입니다."), + + // 403 Forbidden - 인증 거부 + AUTHENTICATION_DENIED(FORBIDDEN, "AUTH403_1", "인증이 거부 되었습니다."), + + // 404 Not Found - 찾을 수 없음 + REFRESH_TOKEN_NOT_FOUND(NOT_FOUND, "AUTH404_1", "리프레시 토큰이 존재하지 않습니다."), + ; + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ErrorReasonDTO getReason() { + return ErrorReasonDTO.builder() + .message(message) + .code(code) + .isSuccess(false) + .build(); + } + + @Override + public ErrorReasonDTO getReasonHttpStatus() { + return ErrorReasonDTO.builder() + .message(message) + .code(code) + .httpStatus(httpStatus) + .isSuccess(false) + .build(); + } +} diff --git a/src/main/java/org/example/tree/global/exception/AuthException.java b/src/main/java/org/example/tree/global/exception/AuthException.java new file mode 100644 index 0000000..45ccbcb --- /dev/null +++ b/src/main/java/org/example/tree/global/exception/AuthException.java @@ -0,0 +1,8 @@ +package org.example.tree.global.exception; + +public class AuthException extends GeneralException{ + + public AuthException(BaseErrorCode errorCode) { + super(errorCode); + } +} diff --git a/src/main/java/org/example/tree/global/exception/BaseErrorCode.java b/src/main/java/org/example/tree/global/exception/BaseErrorCode.java new file mode 100644 index 0000000..dd501c2 --- /dev/null +++ b/src/main/java/org/example/tree/global/exception/BaseErrorCode.java @@ -0,0 +1,8 @@ +package org.example.tree.global.exception; + +public interface BaseErrorCode { + + public ErrorReasonDTO getReason(); + + public ErrorReasonDTO getReasonHttpStatus(); +} diff --git a/src/main/java/org/example/tree/global/exception/ErrorReasonDTO.java b/src/main/java/org/example/tree/global/exception/ErrorReasonDTO.java new file mode 100644 index 0000000..20bbd0a --- /dev/null +++ b/src/main/java/org/example/tree/global/exception/ErrorReasonDTO.java @@ -0,0 +1,16 @@ +package org.example.tree.global.exception; + +import lombok.Builder; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@Builder +public class ErrorReasonDTO { + + private HttpStatus httpStatus; + + private final boolean isSuccess; + private final String code; + private final String message; +} diff --git a/src/main/java/org/example/tree/global/exception/GeneralException.java b/src/main/java/org/example/tree/global/exception/GeneralException.java index f8f1196..6877cf8 100644 --- a/src/main/java/org/example/tree/global/exception/GeneralException.java +++ b/src/main/java/org/example/tree/global/exception/GeneralException.java @@ -6,5 +6,14 @@ @Getter @AllArgsConstructor public class GeneralException extends RuntimeException { -private final GlobalErrorCode errorCode; + + private final BaseErrorCode errorCode; + + public ErrorReasonDTO getErrorReason() { + return this.errorCode.getReason(); + } + + public ErrorReasonDTO getErrorReasonHttpStatus() { + return this.errorCode.getReasonHttpStatus(); + } } diff --git a/src/main/java/org/example/tree/global/exception/GeneralExceptionHandler.java b/src/main/java/org/example/tree/global/exception/GeneralExceptionHandler.java index 9079cc1..d80ad57 100644 --- a/src/main/java/org/example/tree/global/exception/GeneralExceptionHandler.java +++ b/src/main/java/org/example/tree/global/exception/GeneralExceptionHandler.java @@ -96,21 +96,22 @@ public ResponseEntity onThrowException( GeneralException generalException, @AuthenticationPrincipal User user, HttpServletRequest request) { - getExceptionStackTrace(generalException, user, request); - GlobalErrorCode errorCode = generalException.getErrorCode(); - return handleExceptionInternal(generalException, errorCode, null, request); +// getExceptionStackTrace(generalException, user, request); +// GlobalErrorCode errorCode = generalException.getErrorCode(); + ErrorReasonDTO errorReasonHttpStatus = generalException.getErrorReasonHttpStatus(); + return handleExceptionInternal(generalException, errorReasonHttpStatus, null, request); } private ResponseEntity handleExceptionInternal( - Exception e, GlobalErrorCode errorCode, HttpHeaders headers, HttpServletRequest request) { + Exception e, ErrorReasonDTO reason, HttpHeaders headers, HttpServletRequest request) { ApiResponse body = - ApiResponse.onFailure(errorCode, null); + ApiResponse.onFailure(reason.getCode(), reason.getMessage(), null); e.printStackTrace(); WebRequest webRequest = new ServletWebRequest(request); return super.handleExceptionInternal( - e, body, headers, errorCode.getHttpStatus(), webRequest); + e, body, headers, reason.getHttpStatus(), webRequest); } private ResponseEntity handleExceptionInternalFalse( @@ -121,7 +122,7 @@ private ResponseEntity handleExceptionInternalFalse( WebRequest request, String errorPoint) { ApiResponse body = - ApiResponse.onFailure(errorCode, errorPoint); + ApiResponse.onFailure(errorCode.getCode(), errorCode.getMessage(), errorPoint); return super.handleExceptionInternal(e, body, headers, status, request); } @@ -132,14 +133,14 @@ private ResponseEntity handleExceptionInternalArgs( WebRequest request, Map errorArgs) { ApiResponse body = - ApiResponse.onFailure(errorCode, errorArgs); + ApiResponse.onFailure(errorCode.getCode(), errorCode.getMessage(), errorArgs); return super.handleExceptionInternal(e, body, headers, errorCode.getHttpStatus(), request); } private ResponseEntity handleExceptionInternalConstraint( Exception e, GlobalErrorCode errorCode, HttpHeaders headers, WebRequest request) { ApiResponse body = - ApiResponse.onFailure(errorCode, null); + ApiResponse.onFailure(errorCode.getCode(), errorCode.getMessage(), null); return super.handleExceptionInternal(e, body, headers, errorCode.getHttpStatus(), request); } diff --git a/src/main/java/org/example/tree/global/exception/GlobalErrorCode.java b/src/main/java/org/example/tree/global/exception/GlobalErrorCode.java index 26c74bb..4e20fc4 100644 --- a/src/main/java/org/example/tree/global/exception/GlobalErrorCode.java +++ b/src/main/java/org/example/tree/global/exception/GlobalErrorCode.java @@ -9,71 +9,84 @@ @Getter @AllArgsConstructor -public enum GlobalErrorCode { +public enum GlobalErrorCode implements BaseErrorCode{ - // Server Error - SERVER_ERROR(INTERNAL_SERVER_ERROR,"서버 에러, 서버 개발자에게 알려주세요."), + // 500 Server Error + SERVER_ERROR(INTERNAL_SERVER_ERROR, "GLOBAL500_1", "서버 에러, 서버 개발자에게 알려주세요."), // Args Validation Error - BAD_ARGS_ERROR(BAD_REQUEST, "request body의 validation이 실패했습니다. 응답 body를 참고해주세요"), + BAD_ARGS_ERROR(BAD_REQUEST, "GLOBAL400_1", "request body의 validation이 실패했습니다. 응답 body를 참고해주세요"), // Member // 400 BAD_REQUEST - 잘못된 요청 - NOT_VALID_PHONE_NUMBER(BAD_REQUEST, "유효하지 않은 전화번호 입니다."), + NOT_VALID_PHONE_NUMBER(BAD_REQUEST, "USER400_1", "유효하지 않은 전화번호 입니다."), // 401 Unauthorized - 권한 없음 - INVALID_TOKEN(UNAUTHORIZED, "토큰이 유효하지 않습니다."), - INVALID_REFRESH_TOKEN(UNAUTHORIZED, "리프레시 토큰이 유효하지 않습니다."), - LOGIN_REQUIRED(UNAUTHORIZED, "로그인이 필요한 서비스입니다."), + // 403 Forbidden - 인증 거부 - AUTHENTICATION_DENIED(FORBIDDEN, "인증이 거부 되었습니다."), - AUTHENTICATION_REQUIRED(UNAUTHORIZED, "인증 정보가 유효하지 않습니다."), + // 404 Not Found - 찾을 수 없음 - NEED_AGREE_REQUIRE_TERMS(NOT_FOUND, "필수 약관에 동의해 주세요."), - MEMBER_NOT_FOUND(NOT_FOUND, "등록된 사용자 정보가 없습니다."), - REFRESH_TOKEN_NOT_FOUND(NOT_FOUND, "리프레시 토큰이 존재하지 않습니다."), - REFRESH_TOKEN_EXPIRED(NOT_FOUND, "리프레시 토큰이 만료 되었습니다."), - TOKEN_EXPIRED(NOT_FOUND, "토큰이 만료 되었습니다."), + MEMBER_NOT_FOUND(NOT_FOUND, "USER404_1", "등록된 사용자 정보가 없습니다."), + + // 409 CONFLICT : Resource 를 찾을 수 없음 - DUPLICATE_PHONE_NUMBER(CONFLICT, "중복된 전화번호가 존재합니다."), + DUPLICATE_PHONE_NUMBER(CONFLICT, "USER409_1", "중복된 전화번호가 존재합니다."), //Profile //404 Not Found - 찾을 수 없음 - PROFILE_NOT_FOUND(NOT_FOUND, "존재하지 않는 프로필입니다."), - AVAILABLE_PROFILE_NOT_FOUND(NOT_FOUND, "현재 선택된 프로필이 없습니다."), + PROFILE_NOT_FOUND(NOT_FOUND, "MEMBER404_1", "존재하지 않는 프로필입니다."), + AVAILABLE_PROFILE_NOT_FOUND(NOT_FOUND, "MEMBER404_2", "현재 선택된 프로필이 없습니다."), - //Tree + //Treehouse //404 Not Found - 찾을 수 없음 - TREE_NOT_FOUND(NOT_FOUND, "존재하지 않는 트리입니다."), + TREEHOUSE_NOT_FOUND(NOT_FOUND, "TREEHOUSE404_1", "존재하지 않는 트리입니다."), //Invitation //404 Not Found - 찾을 수 없음 - INVITATION_NOT_FOUND(NOT_FOUND, "존재하지 않는 초대장입니다."), + INVITATION_NOT_FOUND(NOT_FOUND, "INVITATION404_1", "존재하지 않는 초대장입니다."), //Post //404 Not Found - 찾을 수 없음 - POST_NOT_FOUND(NOT_FOUND, "존재하지 않는 게시글입니다."), + POST_NOT_FOUND(NOT_FOUND, "POST404_1", "존재하지 않는 게시글입니다."), //Comment //404 Not Found - 찾을 수 없음 - COMMENT_NOT_FOUND(NOT_FOUND, "존재하지 않는 댓글입니다."), + COMMENT_NOT_FOUND(NOT_FOUND, "COMMENT404_1", "존재하지 않는 댓글입니다."), //Reply //404 Not Found - 찾을 수 없음 - REPLY_NOT_FOUND(NOT_FOUND, "존재하지 않는 답글입니다."), + REPLY_NOT_FOUND(NOT_FOUND, "REPLY404_1", "존재하지 않는 답글입니다."), //Branch //404 Not Found - 찾을 수 없음 - BRANCH_NOT_FOUND(NOT_FOUND, "브랜치 정보를 찾을 수 없습니다."), + BRANCH_NOT_FOUND(NOT_FOUND, "BRANCH404_1", "브랜치 정보를 찾을 수 없습니다."), //Notification //404 Not Found - 찾을 수 없음 - NOTIFICATION_NOT_FOUND(NOT_FOUND, "알림이 없습니다."), + NOTIFICATION_NOT_FOUND(NOT_FOUND, "NOTIFICATION", "존재하지 않는 알림입니다."), ; - - - private final HttpStatus httpStatus; + private final String code; private final String message; + + @Override + public ErrorReasonDTO getReason() { + return ErrorReasonDTO.builder() + .message(message) + .code(code) + .isSuccess(false) + .build(); + } + + @Override + public ErrorReasonDTO getReasonHttpStatus() { + return ErrorReasonDTO.builder() + .message(message) + .code(code) + .httpStatus(httpStatus) + .isSuccess(false) + .build(); + } + } diff --git a/src/main/java/org/example/tree/global/exception/JwtAuthenticationException.java b/src/main/java/org/example/tree/global/exception/JwtAuthenticationException.java index dbf1339..b2933ff 100644 --- a/src/main/java/org/example/tree/global/exception/JwtAuthenticationException.java +++ b/src/main/java/org/example/tree/global/exception/JwtAuthenticationException.java @@ -4,7 +4,7 @@ public class JwtAuthenticationException extends AuthenticationException { - public JwtAuthenticationException(GlobalErrorCode code) { + public JwtAuthenticationException(AuthErrorCode code) { super(code.name()); } } diff --git a/src/main/java/org/example/tree/global/security/filter/JwtAuthFilter.java b/src/main/java/org/example/tree/global/security/filter/JwtAuthFilter.java index 53ccca1..c893bc4 100644 --- a/src/main/java/org/example/tree/global/security/filter/JwtAuthFilter.java +++ b/src/main/java/org/example/tree/global/security/filter/JwtAuthFilter.java @@ -9,6 +9,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.example.tree.global.common.ApiResponse; +import org.example.tree.global.exception.AuthErrorCode; import org.example.tree.global.exception.GlobalErrorCode; import org.example.tree.global.security.provider.TokenProvider; import org.springframework.http.HttpStatus; @@ -63,12 +64,15 @@ private String getRefreshTokenFromRequest(HttpServletRequest request) { // JWT 인증과 관련된 예외 처리 - public void jwtExceptionHandler(HttpServletResponse response, String msg, int statusCode) { - response.setStatus(statusCode); + public void jwtExceptionHandler(HttpServletResponse response, AuthErrorCode errorCode) { + response.setStatus(errorCode.getHttpStatus().value()); response.setContentType("application/json"); try { - String json = new ObjectMapper().writeValueAsString(ApiResponse.onFailure(GlobalErrorCode.INVALID_TOKEN, msg)); + // AuthErrorCode로부터 code와 message 추출 + String code = errorCode.getCode(); + String message = errorCode.getMessage(); + String json = new ObjectMapper().writeValueAsString(ApiResponse.onFailure(code, message, null)); // ApiResponse 객체를 JSON으로 변환 response.getWriter().write(json); } catch (Exception e) { log.error(e.getMessage()); diff --git a/src/main/java/org/example/tree/global/security/handler/JwtAccessDeniedHandler.java b/src/main/java/org/example/tree/global/security/handler/JwtAccessDeniedHandler.java index 3aedc4b..3196294 100644 --- a/src/main/java/org/example/tree/global/security/handler/JwtAccessDeniedHandler.java +++ b/src/main/java/org/example/tree/global/security/handler/JwtAccessDeniedHandler.java @@ -4,6 +4,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.example.tree.global.common.ApiResponse; +import org.example.tree.global.exception.AuthErrorCode; import org.example.tree.global.exception.GlobalErrorCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,7 +29,10 @@ public void handle( response.setStatus(403); PrintWriter writer = response.getWriter(); - ApiResponse apiErrorResult = ApiResponse.onFailure(GlobalErrorCode.AUTHENTICATION_DENIED, ""); + // AuthErrorCode.AUTHENTICATION_DENIED enum에서 코드와 메시지를 얻음 + String code = AuthErrorCode.AUTHENTICATION_DENIED.getCode(); + String message = AuthErrorCode.AUTHENTICATION_DENIED.getMessage(); + ApiResponse apiErrorResult = ApiResponse.onFailure(code, message, null); writer.write(apiErrorResult.toString()); writer.flush(); writer.close(); diff --git a/src/main/java/org/example/tree/global/security/handler/JwtAuthenticationEntryPoint.java b/src/main/java/org/example/tree/global/security/handler/JwtAuthenticationEntryPoint.java index 76acfef..23c4d65 100644 --- a/src/main/java/org/example/tree/global/security/handler/JwtAuthenticationEntryPoint.java +++ b/src/main/java/org/example/tree/global/security/handler/JwtAuthenticationEntryPoint.java @@ -4,6 +4,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.example.tree.global.common.ApiResponse; +import org.example.tree.global.exception.AuthErrorCode; import org.example.tree.global.exception.GlobalErrorCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,7 +29,10 @@ public void commence( response.setStatus(401); PrintWriter writer = response.getWriter(); - ApiResponse apiErrorResult = ApiResponse.onFailure(GlobalErrorCode.AUTHENTICATION_REQUIRED, ""); + // AuthErrorCode.AUTHENTICATION_REQUIRED enum에서 코드와 메시지를 얻음 + String code = AuthErrorCode.AUTHENTICATION_REQUIRED.getCode(); + String message = AuthErrorCode.AUTHENTICATION_REQUIRED.getMessage(); + ApiResponse apiErrorResult = ApiResponse.onFailure(code, message, null); writer.write(apiErrorResult.toString()); writer.flush(); diff --git a/src/main/java/org/example/tree/global/security/handler/JwtAuthenticationExceptionHandler.java b/src/main/java/org/example/tree/global/security/handler/JwtAuthenticationExceptionHandler.java index ade13ae..2d0ded0 100644 --- a/src/main/java/org/example/tree/global/security/handler/JwtAuthenticationExceptionHandler.java +++ b/src/main/java/org/example/tree/global/security/handler/JwtAuthenticationExceptionHandler.java @@ -5,6 +5,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.example.tree.global.common.ApiResponse; +import org.example.tree.global.exception.AuthErrorCode; import org.example.tree.global.exception.GlobalErrorCode; import org.example.tree.global.exception.JwtAuthenticationException; import org.example.tree.global.exception.JwtReissueException; @@ -28,7 +29,8 @@ protected void doFilterInternal( PrintWriter writer = response.getWriter(); String errorCodeName = authException.getMessage(); - ApiResponse apiErrorResult = ApiResponse.onFailure(GlobalErrorCode.valueOf(errorCodeName), ""); + AuthErrorCode errorCode = AuthErrorCode.valueOf(errorCodeName); + ApiResponse apiErrorResult = ApiResponse.onFailure(errorCode.getCode(),errorCode.getMessage(), null); writer.write(apiErrorResult.toString()); writer.flush(); diff --git a/src/main/java/org/example/tree/global/security/provider/TokenProvider.java b/src/main/java/org/example/tree/global/security/provider/TokenProvider.java index 0cc5ffc..b9f3350 100644 --- a/src/main/java/org/example/tree/global/security/provider/TokenProvider.java +++ b/src/main/java/org/example/tree/global/security/provider/TokenProvider.java @@ -8,9 +8,7 @@ import lombok.extern.slf4j.Slf4j; import org.example.tree.domain.member.entity.Member; import org.example.tree.domain.member.service.MemberQueryService; -import org.example.tree.global.exception.GeneralException; -import org.example.tree.global.exception.GlobalErrorCode; -import org.example.tree.global.exception.JwtAuthenticationException; +import org.example.tree.global.exception.*; import org.example.tree.global.redis.service.RedisService; import org.springframework.beans.factory.annotation.Value; @@ -119,7 +117,7 @@ public boolean validateToken(String token) { log.error("Invalid JWT signature, 유효하지 않는 JWT 서명 입니다."); } catch (ExpiredJwtException e) { log.info("Expired JWT token, 만료된 JWT token 입니다."); - throw new JwtAuthenticationException(GlobalErrorCode.TOKEN_EXPIRED); + throw new JwtAuthenticationException(AuthErrorCode.TOKEN_EXPIRED); } catch (UnsupportedJwtException e) { log.error("Unsupported JWT token, 지원되지 않는 JWT 토큰 입니다."); @@ -137,20 +135,20 @@ public void validateRefreshToken(String refreshToken){ Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(refreshToken); } catch (SecurityException | MalformedJwtException e) { log.error("Invalid JWT signature, 유효하지 않는 JWT 서명 입니다."); - throw new GeneralException(GlobalErrorCode.INVALID_TOKEN); + throw new AuthException(AuthErrorCode.INVALID_TOKEN); } catch (ExpiredJwtException e) { log.info("Expired JWT token, 만료된 JWT 리프레시 token 입니다."); - throw new GeneralException(GlobalErrorCode.REFRESH_TOKEN_EXPIRED); + throw new AuthException(AuthErrorCode.REFRESH_TOKEN_EXPIRED); } catch (UnsupportedJwtException e) { log.error("Unsupported JWT token, 지원되지 않는 JWT 토큰 입니다."); - throw new GeneralException(GlobalErrorCode.INVALID_TOKEN); + throw new AuthException(AuthErrorCode.INVALID_TOKEN); } catch (IllegalArgumentException e) { log.error("JWT claims is empty, 잘못된 JWT 토큰 입니다."); - throw new GeneralException(GlobalErrorCode.INVALID_TOKEN); + throw new AuthException(AuthErrorCode.INVALID_TOKEN); } catch (io.jsonwebtoken.security.SignatureException e){ log.error("Invalid JWT signature, 유효하지 않는 JWT 서명 입니다"); - throw new GeneralException(GlobalErrorCode.INVALID_TOKEN); + throw new AuthException(AuthErrorCode.INVALID_TOKEN); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 9ca6099..4e1bace 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -65,8 +65,8 @@ jwt: key: ${JWT_SECRET} # secret : ${JWT_SECRET} authorities-key: authoritiesKey - access-token-validity-in-seconds: 300000 # 30 m - refresh-token-validity-in-seconds: 360000 # 14 d + access-token-validity-in-seconds: 300000 # 5 m + refresh-token-validity-in-seconds: 360000 # 6 d