Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: exception handling #34

Merged
merged 16 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
3d1e4bb
feat: ์ „์—ญ ์˜ˆ์™ธ ํ•ธ๋“ค๋Ÿฌ ์ถ”๊ฐ€
Sangwook02 Jan 30, 2024
cf4c105
feat: ErrorResponse ์ถ”๊ฐ€
Sangwook02 Jan 30, 2024
b24421b
feat: ErrorCode ์ถ”๊ฐ€
Sangwook02 Jan 30, 2024
0ef924e
feat: CustomException ์ถ”๊ฐ€
Sangwook02 Jan 30, 2024
c120e8b
feat: JwtFilter์˜ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ํ•„ํ„ฐ ์ถ”๊ฐ€
Sangwook02 Jan 30, 2024
496e10b
refactor: ํ† ํฐ ํŒŒ์‹ฑ ๊ณผ์ •์˜ ์˜ˆ์™ธ๋ฅผ CustomException์œผ๋กœ ๋ณ€๊ฒฝ
Sangwook02 Jan 30, 2024
464f99b
remove: CustomException ๋˜์ง€๋ฏ€๋กœ throws ์ œ๊ฑฐ
Sangwook02 Jan 30, 2024
f9b56a7
refactor: parseAccessToken ๋‚ด๋ถ€์—์„œ CustomException ๋˜์ง€๋ฏ€๋กœ try-catch ์ œ๊ฑฐ
Sangwook02 Jan 30, 2024
704be5a
typo: ์˜คํƒ€ ์ˆ˜์ •
Sangwook02 Jan 30, 2024
148dd72
feat: Jwt ๊ด€๋ จ ErrorCode ์ถ”๊ฐ€
Sangwook02 Jan 30, 2024
8cc1265
feat: JwtFilter์˜ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ํ•„ํ„ฐ ์ถ”๊ฐ€
Sangwook02 Jan 30, 2024
3cdf2d6
fix: ์žฌ๋ฐœ๊ธ‰ ๋ฉ”์„œ๋“œ์—์„œ ์žก์„ ์ˆ˜ ์žˆ๋„๋ก ์ˆ˜์ •
Sangwook02 Jan 30, 2024
92092e4
Merge branch 'develop' of https://github.com/GDSC-Hongik/gdsc-server โ€ฆ
Sangwook02 Jan 30, 2024
19fb017
fix: try-catch๋กœ ๋งŒ๋ฃŒ ํ† ํฐ์˜ ๊ฒฝ์šฐ null์„ ๋ฐ˜ํ™˜
Sangwook02 Jan 30, 2024
ca82579
fix: ํ† ํฐ ์กฐํšŒ ๋ฉ”์„œ๋“œ ์ˆ˜์ •
Sangwook02 Jan 31, 2024
9c63448
fix: refreshToken์„ accessToken์œผ๋กœ ์ˆ˜์ •
Sangwook02 Jan 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private void saveRefreshTokenToRedis(RefreshTokenDto refreshTokenDto) {
public AccessTokenDto retrieveAccessToken(String accessTokenValue) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

์ œ๊ฑฐ ์˜๋„๊ฐ€ ๊ถ๊ธˆํ•ฉ๋‹ˆ๋‹ค~

Copy link
Member Author

@Sangwook02 Sangwook02 Jan 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

์ด ๋ถ€๋ถ„์€ ์ œ๊ฐ€ ์ฐฉ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.
์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•˜๋Š” ๊ฒŒ ์ข‹๊ฒ ๋„ค์š”

try {
      return jwtUtil.parseAccessToken(accessTokenValue);
} catch (ExpiredJwtException e) {
      return null;
}

try {
return jwtUtil.parseAccessToken(accessTokenValue);
} catch (Exception e) {
} catch (ExpiredJwtException e) {
return null;
}
}
Expand All @@ -71,7 +71,7 @@ public RefreshTokenDto retrieveRefreshToken(String refreshTokenValue) {

private Optional<RefreshToken> getRefreshTokenFromRedis(Long memberId) {
// TODO: CustomException์œผ๋กœ ๋ฐ”๊พธ๊ธฐ
return refreshTokenRepository.findByMemberId(memberId);
return refreshTokenRepository.findById(memberId);
}

private RefreshTokenDto parseRefreshToken(String refreshTokenValue) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.gdschongik.gdsc.domain.auth.dao;

import com.gdschongik.gdsc.domain.auth.domain.RefreshToken;
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;

public interface RefreshTokenRepository extends CrudRepository<RefreshToken, Long> {
Optional<RefreshToken> findByMemberId(Long aLong);
}
public interface RefreshTokenRepository extends CrudRepository<RefreshToken, Long> {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import static org.springframework.security.config.Customizer.*;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gdschongik.gdsc.domain.auth.application.JwtService;
import com.gdschongik.gdsc.domain.member.dao.MemberRepository;
import com.gdschongik.gdsc.global.security.CustomSuccessHandler;
import com.gdschongik.gdsc.global.security.CustomUserService;
import com.gdschongik.gdsc.global.security.JwtExceptionFilter;
import com.gdschongik.gdsc.global.security.JwtFilter;
import com.gdschongik.gdsc.global.util.CookieUtil;
import lombok.RequiredArgsConstructor;
Expand All @@ -26,6 +28,7 @@ public class WebSecurityConfig {
private final MemberRepository memberRepository;
private final JwtService jwtService;
private final CookieUtil cookieUtil;
private final ObjectMapper objectMapper;

@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
Expand All @@ -41,6 +44,7 @@ public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Excepti
.successHandler(customSuccessHandler(jwtService, cookieUtil)));

httpSecurity.addFilterBefore(jwtFilter(jwtService, cookieUtil), UsernamePasswordAuthenticationFilter.class);
httpSecurity.addFilterBefore(jwtExceptionFilter(objectMapper), JwtFilter.class);

return httpSecurity.build();
}
Expand All @@ -59,4 +63,9 @@ public CustomSuccessHandler customSuccessHandler(JwtService jwtService, CookieUt
public JwtFilter jwtFilter(JwtService jwtService, CookieUtil cookieUtil) {
return new JwtFilter(jwtService, cookieUtil);
}

@Bean
public JwtExceptionFilter jwtExceptionFilter(ObjectMapper objectMapper) {
return new JwtExceptionFilter(objectMapper);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.gdschongik.gdsc.global.exception;

import lombok.Getter;

@Getter
public class CustomException extends RuntimeException {

private final ErrorCode errorCode;

public CustomException(ErrorCode errorCode) {
super(errorCode.getMessage());
this.errorCode = errorCode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.gdschongik.gdsc.global.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum ErrorCode {
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "์„œ๋ฒ„ ์—๋Ÿฌ์ž…๋‹ˆ๋‹ค."),

// Jwt
INVALID_JWT_TOKEN(HttpStatus.UNAUTHORIZED, "์œ ํšจํ•˜์ง€ ์•Š์€ JWT ํ† ํฐ์ž…๋‹ˆ๋‹ค."),
EXPIRED_JWT_TOKEN(HttpStatus.UNAUTHORIZED, "๋งŒ๋ฃŒ๋œ JWT ํ† ํฐ์ž…๋‹ˆ๋‹ค.");

private final HttpStatus status;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.gdschongik.gdsc.global.exception;

public record ErrorResponse(String errorCodeName, String errorMessage) {
public static ErrorResponse of(ErrorCode errorCode) {
return new ErrorResponse(errorCode.name(), errorCode.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.gdschongik.gdsc.global.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

@ExceptionHandler(CustomException.class)
public ResponseEntity<ErrorResponse> handleCustomException(CustomException e) {
log.error("CustomException : {}", e.getMessage(), e);
return ResponseEntity.status(e.getErrorCode().getStatus()).body(ErrorResponse.of(e.getErrorCode()));
}

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
log.error("INTERNAL_SERVER_ERROR : {}", e.getMessage(), e);
return ResponseEntity.status(ErrorCode.INTERNAL_SERVER_ERROR.getStatus())
.body(ErrorResponse.of(ErrorCode.INTERNAL_SERVER_ERROR));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.gdschongik.gdsc.global.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.global.exception.ErrorResponse;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.filter.OncePerRequestFilter;

@Slf4j
@RequiredArgsConstructor
public class JwtExceptionFilter extends OncePerRequestFilter {

private final ObjectMapper objectMapper;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} catch (CustomException e) {
log.error("JWTException : {}", e.getMessage(), e);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.setStatus(e.getErrorCode().getStatus().value());
response.getWriter().write(objectMapper.writeValueAsString(ErrorResponse.of(e.getErrorCode())));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
}

Optional<AccessTokenDto> reissueAccessToken =
Optional.ofNullable(jwtService.reissueAccessTokenIfExpired(refreshTokenValue));
Optional.ofNullable(jwtService.reissueAccessTokenIfExpired(accessTokenValue));
RefreshTokenDto refreshTokenDto = jwtService.retrieveRefreshToken(refreshTokenValue);

// AT๊ฐ€ ๋งŒ๋ฃŒ๋˜์—ˆ๊ณ , RT๊ฐ€ ์œ ํšจํ•˜๋ฉด AT, RT ์žฌ๋ฐœ๊ธ‰
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import com.gdschongik.gdsc.domain.auth.dto.RefreshTokenDto;
import com.gdschongik.gdsc.domain.member.domain.MemberRole;
import com.gdschongik.gdsc.global.common.constant.JwtConstant;
import com.gdschongik.gdsc.global.exception.CustomException;
import com.gdschongik.gdsc.global.exception.ErrorCode;
import com.gdschongik.gdsc.global.property.JwtProperty;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
Expand Down Expand Up @@ -83,7 +85,7 @@ public AccessTokenDto parseAccessToken(String accessTokenValue) throws ExpiredJw
} catch (ExpiredJwtException e) {
throw e;
} catch (Exception e) {
return null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null๋กœ ๋ฆฌํ„ดํ•˜๋Š”๊ฑฐ ์˜๋„ํ•œ ๋ถ€๋ถ„์ธ๋ฐ ์ˆ˜์ •ํ•œ ์ด์œ ๊ฐ€ ์žˆ๋‚˜์š”?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๋งŒ๋ฃŒ ์ด์™ธ์˜ ์˜ˆ์™ธ๋Š” null์„ ๋ฐ˜ํ™˜ํ•ด์„œ ์žฌ๋ฐœ๊ธ‰ ๊ณผ์ •์„ ๊ฑฐ์น˜๊ธฐ๋ณด๋‹ค ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋Š” ๊ฒƒ์ด ์ ์ ˆํ•œ ๊ฒƒ ๊ฐ™์•„ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

๋งŒ๋ฃŒ ์ด์™ธ์˜ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ด์„œ JwtFilter ์˜ line 52 ๋กœ์ง์„ ํƒ€๋”๋ผ๋„, reissueAccessTokenIfExpired ์—์„œ ExpiredJwtException ์— ์žกํžˆ์ง€ ์•Š์•„ null๋กœ ๋ฐ˜ํ™˜๋˜๊ธฐ ๋•Œ๋ฌธ์— isPresent ๋กœ์ง์„ ํƒ€์ง€ ์•Š์•„์„œ ์žฌ๋ฐœ๊ธ‰์ด ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋กœ์ง ๋‹ค์‹œ ํ•œ๋ฒˆ ์ฒดํฌํ•ด๋ณด์‹ค๋ž˜์š”?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

์žกํžˆ์ง€ ์•Š๋Š” ์ด์œ ๋ฅผ ํ™•์ธํ•ด๋ณธ ๊ฒฐ๊ณผ,
reissueAccessTokenIfExpired์— AT๊ฐ€ ์•„๋‹Œ RT๋ฅผ ๋„˜๊ฒจ์„œ ExpiredJwtException์ด ์•„๋‹Œ SignatureException์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด ์›์ธ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

์ด์™ธ์—๋„ JwtService.getRefreshTokenFromRedis์˜ ๋‚ด๋ถ€๋ฅผ ์กฐ๊ธˆ ์ˆ˜์ •ํ–ˆ์œผ๋‹ˆ ๊ฐ™์ด ํ™•์ธ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

throw new CustomException(ErrorCode.INVALID_JWT_TOKEN);
}
}

Expand Down
Loading