From 9d42fa81e2a422d27c93a76b6a9e26b514ca2517 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EC=86=90=ED=99=8D=EC=84=9D?=
 <78216059+bayy1216@users.noreply.github.com>
Date: Sat, 1 Jun 2024 03:05:21 +0900
Subject: [PATCH] =?UTF-8?q?[Fix]:=20jwt=20=EC=97=90=EB=9F=AC=20=EC=9D=91?=
 =?UTF-8?q?=EB=8B=B5=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=88=84=EB=9D=BD=20fix?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../global/api/ApiControllerAdvice.java       |  1 -
 .../global/security/WebSecurityConfig.java    | 46 ++++++++++++-------
 2 files changed, 29 insertions(+), 18 deletions(-)

diff --git a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/api/ApiControllerAdvice.java b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/api/ApiControllerAdvice.java
index 9eddca6..2e56b8f 100644
--- a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/api/ApiControllerAdvice.java
+++ b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/api/ApiControllerAdvice.java
@@ -1,6 +1,5 @@
 package org.haedal.zzansuni.global.api;
 
-import io.jsonwebtoken.JwtException;
 import jakarta.validation.ConstraintViolation;
 import jakarta.validation.ConstraintViolationException;
 import lombok.extern.slf4j.Slf4j;
diff --git a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/security/WebSecurityConfig.java b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/security/WebSecurityConfig.java
index 091f4a2..a276712 100644
--- a/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/security/WebSecurityConfig.java
+++ b/zzansuni-api-server/app/src/main/java/org/haedal/zzansuni/global/security/WebSecurityConfig.java
@@ -1,8 +1,11 @@
 package org.haedal.zzansuni.global.security;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
 import org.haedal.zzansuni.core.api.ApiResponse;
+import org.haedal.zzansuni.global.exception.UnauthorizedException;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.http.MediaType;
@@ -21,6 +24,7 @@
 import org.springframework.web.cors.CorsConfigurationSource;
 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
 
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
 
@@ -78,23 +82,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
          * 2. 인가 예외 처리 (권한이 없는 사용자) 403
          */
         http.exceptionHandling((exception) -> exception
-                .authenticationEntryPoint((request, response, authException) -> {
-                    var errorResponse = ApiResponse.fail("UNAUTHORIZED", "인증이 필요합니다.");
-                    var json = objectMapper.writeValueAsString(errorResponse);
-                    response.setStatus(401);
-                    response.setContentType(MediaType.APPLICATION_JSON_VALUE);
-                    response.getWriter().write(json);
-                    response.getWriter().flush();
-                })
-                .accessDeniedHandler((request, response, accessDeniedException) -> {
-                    var errorResponse = ApiResponse.fail("ACCESS_DENIED", "권한이 없습니다.");
-                    var json = objectMapper.writeValueAsString(errorResponse);
-
-                    response.setStatus(403);
-                    response.setContentType(MediaType.APPLICATION_JSON_VALUE);
-                    response.getWriter().write(json);
-                    response.getWriter().flush();
-                })
+                .authenticationEntryPoint((request, response, authException) -> responseError(response, "UNAUTHORIZED", "인증이 필요합니다."))
+                .accessDeniedHandler((request, response, accessDeniedException) -> responseError(response, "ACCESS_DENIED", "권한이 없습니다."))
         );
 
         /**
@@ -105,9 +94,32 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
          */
         http.addFilterBefore(jwtAuthenticationFilter(authenticationManager()), BasicAuthenticationFilter.class);
 
+        //jwtAuthenticationFilter에서 예외가 발생했을때, 예외를 받는 필터를 추가한다.
+        http.addFilterBefore((servletRequest, servletResponse, filterChain) -> {
+            try {
+                filterChain.doFilter(servletRequest, servletResponse);
+            } catch (UnauthorizedException e) {
+                HttpServletResponse response = (HttpServletResponse) servletResponse;
+                responseError(response, "UNAUTHORIZED", "인증이 필요합니다.");
+            }
+        }, AuthorizationJwtHeaderFilter.class);
+
         return http.build();
     }
 
+    /**
+     * 인증 예외 처리 응답을 생성하는 메서드
+     */
+    private void responseError(HttpServletResponse response, String code, String message) throws IOException {
+        var errorResponse = ApiResponse.fail(code, message);
+        var json = objectMapper.writeValueAsString(errorResponse);
+        response.setStatus(401);
+        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+        response.setCharacterEncoding("UTF-8");
+        response.getWriter().write(json);
+        response.getWriter().flush();
+    }
+
     @Bean
     public AuthenticationManager authenticationManager(){
         return new ProviderManager(List.of(jwtProvider));