개요
과제 프로젝트에서 직접 정의한 문제와 해결 과정을 기록하겠습니다.
Spring Security JWT 적용
1. [문제 인식 및 정의]
기존 방식에서는 `request.setAttribute`를 사용하여 사용자 정보를 저장했습니다.
하지만 HTTP 요청이 끝나면 사용자 정보가 사라지는 문제가 발생했습니다.
JWT는 Stateless한 인증 방식이지만, 기존 request는 Stateful하게 동작하여 비효율적이었습니다.
이를 해결하기 위해 Spring Security를 적용하여 사용자 정보가 Stateless하게 유지되도록 개선했습니다.
2. [해결 방안]
기존의 코드 구조를 최대한 유지하면서 Spring Security를 적용시켰습니다.
3. [해결 완료]
Spring Security 적용 과정을 별도로 작성했습니다.
2025.02.27 - [Java Study/Frameworks] - Spring Security를 활용한 JWT 인증 적용해보기 🖥️🔑
Spring Security를 활용한 JWT 인증 적용해보기 🖥️🔑
필터(JwtFilter)를 직접 등록하고, HttpServletRequest에서 토큰을 추출하는 방식으로 구현되어있던 코드를 Spring Security를 적용하면서 더 안전하고 유지보수가 쉬운 구조로 변경해보았다.이번 포스팅에
mannakingdom.tistory.com
JWT 인증 실패 시 403 Forbiden 반환 문제
1. [문제 인식 및 정의]
`JwtAuthenticationFilter`에서 `ExpiredJwtException`이 발생하면, `response.sendError`로 401 UNAUTHORIZED이 반환 되도록 설정되어있습니다.
그러나 실제 테스트에서는 403 FORBIDEN이 발생하는 문제가 발생했습니다.
Spring Security 내부에서 403을 반환하는게 우선적으로 처리하기 때문이라고 판단했습니다.
인증 과정에서 발생한 예외가 항상 403 Forbidden을 반환하므로, 수정이 필요해 보였습니다.
2. [해결 방안]
2-1. [의사결정 과정]
처음에는 기존에 작성된 `GlobalExceptionHandler`의 `handleAuthException`을 활용해서 해결해보려고 했으나, 이 또한 적용이 안되었습니다.
2-2. [해결 과정]
그래서 이전 과제의 Session으로 인증/인가를 구현했을 때의 상황과 비슷해 보여, Session 방식에서 적용했던 에러 반환 방식을 참고해서 수정을 했습니다.
3. [해결 완료]
3-1. [회고]
기존의 `GloberExceptionHabdler`에서 사용하는 `getErrorResponse`와 동일한 구조로 status, code, message를 JSON 형태로 반환하도록 코드를 수정했습니다.
코드 수정: JwtAuthenticationFilter
- doFilterInternal 메서드
catch (ExpiredJwtException e) {
log.error("만료된 JWT 토큰입니다.", e);
String message = "만료된 JWT 토큰입니다.";
sendErrorResponse(response, HttpStatus.UNAUTHORIZED, message);
return;
}
catch (JwtException | IllegalArgumentException e) {
String message = "유효하지 않은 JWT 토큰입니다.";
log.error(message, e);
sendErrorResponse(response, HttpStatus.UNAUTHORIZED, message);
return;
}
- sendErrorResponse 메서드
private void sendErrorResponse(HttpServletResponse response, HttpStatus status, String message) throws IOException {
Map<String, Object> responseContent = new LinkedHashMap<>();
responseContent.put("status", status.name());
responseContent.put("code", status.value());
responseContent.put("message", message);
ObjectMapper objectMapper = new ObjectMapper();
String jsonResponse = objectMapper.writeValueAsString(responseContent);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.setStatus(status.value());
response.getWriter().write(jsonResponse);
response.getWriter().flush();
}
3-2. [전후 데이터 비교]
예외 처리 방식 통합
1. [문제 인식 및 정의]
`LoggingInterceptor`에서 권한을 체크할 때, 권한이 없는 경우에도 위와 같은 문제가 발생함을 확인했습니다.
2. [해결 방안]
- `JwtAuthenticationFilter`와 동일하게 수정하려고 했으나, 중복된 메서드가 생성되는 문제가 생겼습니다.
- 공용 유틸 클래스(`ResponseUtil`)을 생성하여 예외 응답 처리를 통합하기로 했습니다.
3. [해결 완료]
ResponseUtil을 생성하여 sendErrorResponse를 공용 메서드로 분리했습니다.
public class ResponseUtil {
private static final ObjectMapper objectMapper = new ObjectMapper();
public static void sendErrorResponse(HttpServletResponse response, HttpStatus status, String message) throws IOException {
Map<String, Object> responseContent = new LinkedHashMap<>();
responseContent.put("status", status.name());
responseContent.put("code", status.value());
responseContent.put("message", message);
String jsonResponse = objectMapper.writeValueAsString(responseContent);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.setStatus(status.value());
response.getWriter().write(jsonResponse);
response.getWriter().flush();
}
}
JwtAuthenticationFilter와 LoggingInterceptor에 생성한 메서드를 임포트하여 사용하도록 했습니다.
import static org.example.expert.common.utils.ResponseUtil.sendErrorResponse;
개선 후 응답
4. [추가 이슈]
`AuthContorller`에서 로그인할 때(`signin`) `RequestBody`에서 받은 email이나 password가 유효하지 않을 경우에도 동일한 문제가 발생하는 것을 확인했습니다.
하지만 시간이 부족해서 해당 부분은 수정하지 못했습니다.
마무리
이번 과제는 추가 학습할 부분이 많아서 TIL을 열심히 작성했습니다.
2025.02.20 - [Java Study/Frameworks] - [Spring] 인증/인가와 Session 방식과 JWT 방식의 차이
[Spring] 인증/인가와 Session 방식과 JWT 방식의 차이
인증 / 인가인증 Authentication특정 인증 요청(로그인, 이메일 인증 등)사용자 식별을 하기 위해서 필요ex) 뉴스피드 프로젝트: 로그인인가 Authorization이미 인증된 사용자에게 특정 자원에 대한 접
mannakingdom.tistory.com
2025.02.24 - [Java Study/Frameworks] - git clone 후 build.gradle 빨간 줄❓ 해결 방법‼️
git clone 후 build.gradle 빨간 줄❓ 해결 방법‼️
GitHub에서 프로젝트를 클론한 후 build.gradle 파일에 아래 사진처럼 빨간 줄이 좍좍 그어지는 현상이 발생했다. 1. 문제 상황클론한 프로젝트에서 Gradle Wrapper(gradlew)를 실행하자, 다음과 같은 오류
mannakingdom.tistory.com
2025.02.25 - [Java Study/Frameworks] - Spring JWT 인증 과정 뜯어보기 🔑 | Spring Security 없이 구현하는 방법
Spring JWT 인증 과정 뜯어보기 🔑 | Spring Security 없이 구현하는 방법
JWT의 이론에 대해서는 이미 학습을 했고, Spring Boot에서 JWT를 활용하는 방법을 학습하겠다 🤓 [Spring] 인증/인가와 Session 방식과 JWT 방식의 차이인증 / 인가인증 Authentication특정 인증 요
mannakingdom.tistory.com
2025.02.26 - [Java Study/Frameworks] - Interceptor와 AOP 개념 정리 및 API 로깅 예시 ✍️
Interceptor와 AOP 개념 정리 및 API 로깅 예시 ✍️
📌 InterceptorSpring의 `HandlerInterceptor`를 활용하여 API 요청 전에 권한을 검사하거나 로그를 남길 때 사용`HandlerInterceptor`를 구현하여 요청(Request)과 응답(Response)을 가로채는 방식서버에 들어온 Reque
mannakingdom.tistory.com
2025.02.27 - [Java Study/Frameworks] - Spring Security를 활용한 JWT 인증 적용해보기 🖥️🔑
Spring Security를 활용한 JWT 인증 적용해보기 🖥️🔑
필터(JwtFilter)를 직접 등록하고, HttpServletRequest에서 토큰을 추출하는 방식으로 구현되어있던 코드를 Spring Security를 적용하면서 더 안전하고 유지보수가 쉬운 구조로 변경해보았다.이번 포스팅에
mannakingdom.tistory.com
블로그에 학습한 내용을 기재하느라 시간을 많이 썼지만 공부는 확실히 된 것 같습니다.
https://github.com/mannaKim/spring-advanced
GitHub - mannaKim/spring-advanced
Contribute to mannaKim/spring-advanced development by creating an account on GitHub.
github.com
'TIL (Today I Learned)' 카테고리의 다른 글
H2 데이터베이스 설치 방법 | Server / In-memory / Embedded Mode❔ (0) | 2025.03.10 |
---|---|
스노우플레이크 방식으로 주문번호 생성하기 (0) | 2025.03.10 |
Spring Security를 활용한 JWT 인증 적용해보기 🖥️🔑 (0) | 2025.02.27 |
Interceptor와 AOP 개념 정리 및 API 로깅 예시 ✍️ (0) | 2025.02.26 |
Spring JWT 인증 과정 뜯어보기 🔑 | Spring Security 없이 구현하는 방법 (0) | 2025.02.25 |