diff --git a/src/main/java/com/tikitaka/naechinso/domain/member/MemberService.java b/src/main/java/com/tikitaka/naechinso/domain/member/MemberService.java index b61e082..25827a5 100644 --- a/src/main/java/com/tikitaka/naechinso/domain/member/MemberService.java +++ b/src/main/java/com/tikitaka/naechinso/domain/member/MemberService.java @@ -67,7 +67,7 @@ public MemberLoginResponseDTO forceLogin(Member authMember, MemberLoginRequestDT } /** - * 로그아웃 -> Fcm Token DB에서 삭제한다 + * 로그아웃 -> Register Token 및 Fcm Token DB에서 삭제한다 * */ public MemberLoginResponseDTO logout(Member authMember) { Member member = findByMember(authMember); @@ -77,6 +77,10 @@ public MemberLoginResponseDTO logout(Member authMember) { throw new BadRequestException(ErrorCode.USER_ALREADY_LOGGED_OUT); } + //redis 에서 registerToken 삭제 + jwtTokenProvider.deleteRegisterToken(member.getPhone()); + + //푸시 알림 등록 해제 member.setFcmToken(""); memberRepository.save(member); @@ -85,17 +89,23 @@ public MemberLoginResponseDTO logout(Member authMember) { /** * 로그인 -> Fcm Token DB에 등록한다 + * @// TODO: 2022/10/30 노션에 정리 * */ public TokenResponseDTO reissue(String accessToken, String refreshToken) { String phone; + + if (!jwtTokenProvider.validateTokenExceptExpiration(accessToken)){ + throw new BadRequestException(ErrorCode.INVALID_ACCESS_TOKEN); + } + try { phone = jwtTokenProvider.parseClaims(accessToken).getSubject(); } catch (Exception e) { throw new BadRequestException(ErrorCode.INVALID_REFRESH_TOKEN); } - jwtTokenProvider.validateRefreshToken(phone, refreshToken); Member authMember = findByPhone(phone); + jwtTokenProvider.validateRefreshToken(phone, refreshToken); return jwtTokenProvider.generateToken(new JwtDTO(phone, authMember.getRole().toString())); } diff --git a/src/main/java/com/tikitaka/naechinso/global/config/security/jwt/JwtTokenProvider.java b/src/main/java/com/tikitaka/naechinso/global/config/security/jwt/JwtTokenProvider.java index 1c35b93..d2ad737 100644 --- a/src/main/java/com/tikitaka/naechinso/global/config/security/jwt/JwtTokenProvider.java +++ b/src/main/java/com/tikitaka/naechinso/global/config/security/jwt/JwtTokenProvider.java @@ -20,6 +20,8 @@ import javax.servlet.http.HttpServletRequest; import java.time.Duration; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.*; @Slf4j @@ -118,6 +120,22 @@ public String generateRegisterToken(JwtDTO jwtDTO) return registerToken; } + /** Redis 에서 RegistrerToken 을 제거 + * @param phone 로그아웃 요청 유저 + * @return true if redis 서버에 토큰이 있었을 경우 + * false if 토큰이 없었을 경우 + */ + public boolean deleteRegisterToken(String phone) { + try { + if (redisService.hasKey(phone)) { + redisService.deleteValues(phone); + return true; + } + } catch (Exception e) { + log.error("Redis 로그아웃 요청을 실패했습니다"); + } + return false; + } public Authentication getAuthentication(HttpServletRequest request, String accessToken) { Claims claims = parseClaims(accessToken); @@ -189,6 +207,24 @@ public boolean validateToken(HttpServletRequest request, String token) { } return false; } + /** + * 토큰 예외 중 만료 상황만 검증 함수 + * @param token 검사하려는 JWT 토큰 + * @returns boolean + * */ + public boolean validateTokenExceptExpiration(String token) { + final String encodedKey = Base64.getEncoder().encodeToString(JWT_SECRET.getBytes()); + try { + Jwts.parser().setSigningKey(encodedKey).parseClaimsJws(token); + return true; +// Jws claims = Jwts.parser().setSigningKey(encodedKey).parseClaimsJws(token); +// return claims.getBody().getExpiration().before(new Date()); + } catch(ExpiredJwtException e) { + return true; + } catch (Exception e) { + return false; + } + } /** Redis Memory 의 RefreshToken 과 * User 의 RefreshToken 이 일치하는지 확인 diff --git a/src/main/java/com/tikitaka/naechinso/global/error/ErrorCode.java b/src/main/java/com/tikitaka/naechinso/global/error/ErrorCode.java index 0565332..2c7a7f1 100644 --- a/src/main/java/com/tikitaka/naechinso/global/error/ErrorCode.java +++ b/src/main/java/com/tikitaka/naechinso/global/error/ErrorCode.java @@ -22,6 +22,7 @@ public enum ErrorCode { CANNOT_CREATE_RECOMMEND_REQUEST(INTERNAL_SERVER_ERROR, "C007", "추천사 요청에 실패했습니다"), NOT_MULTIPART_HEADER(BAD_REQUEST, "C008", "Multipart 헤더가 아닙니다"), AMAZON_ACCESS_DENIED(FORBIDDEN, "C009", "Amazon S3 접근이 거부되었습니다"), + MAX_FILE_SIZE_EXCEEDED(BAD_REQUEST, "C010", "허용된 최대 파일 크기를 초과했습니다"), /* DB 관련 오류 */ @@ -48,6 +49,7 @@ public enum ErrorCode { INVALID_USER_TOKEN(UNAUTHORIZED, "AUTH013", "서버에 토큰과 일치하는 정보가 없습니다"), LOGIN_FAILED(UNAUTHORIZED, "AUTH014", "로그인에 실패했습니다"), + INVALID_ACCESS_TOKEN(UNAUTHORIZED, "AUTH015", "유효하지 않은 엑세스 토큰입니다"), /* User 관련 오류 */ diff --git a/src/main/java/com/tikitaka/naechinso/global/error/GlobalExceptionHandler.java b/src/main/java/com/tikitaka/naechinso/global/error/GlobalExceptionHandler.java index 96802cf..dcadb94 100644 --- a/src/main/java/com/tikitaka/naechinso/global/error/GlobalExceptionHandler.java +++ b/src/main/java/com/tikitaka/naechinso/global/error/GlobalExceptionHandler.java @@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import org.springframework.web.multipart.MaxUploadSizeExceededException; import org.springframework.web.multipart.MultipartException; import java.util.Arrays; @@ -108,6 +109,17 @@ protected ResponseEntity handleBadCredentialsException(BadCredent return new ResponseEntity<>(response, HttpStatus.UNAUTHORIZED); } + /** + * 업로드 최대 용량을 초과했을 경우 + */ + @ExceptionHandler({MaxUploadSizeExceededException.class}) + protected ResponseEntity handleMultipartException(MaxUploadSizeExceededException e) { + log.error("handleMaxUploadSizeExceededException", e); + final ErrorResponse response = ErrorResponse.of(ErrorCode.MAX_FILE_SIZE_EXCEEDED); + return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST); + } + + /** * 파일 업로드 시 멀티파트 헤더를 설정하지 않았을때 에러 */ diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 7892517..789e850 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -2,6 +2,11 @@ server: port: 8080 spring: + servlet: + #file upload size + multipart: + max-file-size: 3MB + profiles: active: local # Using POSTGRESQL