diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/.DS_Store differ diff --git a/src/main/java/backend/airo/api/global/security/CustomAuthenticationEntryPoint.java b/src/main/java/backend/airo/api/global/security/CustomAuthenticationEntryPoint.java index c386209..31c9a99 100644 --- a/src/main/java/backend/airo/api/global/security/CustomAuthenticationEntryPoint.java +++ b/src/main/java/backend/airo/api/global/security/CustomAuthenticationEntryPoint.java @@ -18,6 +18,16 @@ public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { + + String requestURI = request.getRequestURI(); + + // OAuth2 authorization 요청은 그대로 통과 + if (requestURI.startsWith("/api/oauth2/authorization/")) { + // OAuth2 authorization 요청을 Spring Security의 OAuth2 처리기로 전달 + response.sendRedirect(requestURI + "?" + request.getQueryString()); + return; + } + response.setCharacterEncoding("UTF-8"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setContentType(MediaType.APPLICATION_JSON_VALUE); diff --git a/src/main/java/backend/airo/application/auth/oauth2/usecase/OAuth2AuthenticationUseCase.java b/src/main/java/backend/airo/application/auth/oauth2/usecase/OAuth2AuthenticationUseCase.java index cb2d11b..c88db46 100644 --- a/src/main/java/backend/airo/application/auth/oauth2/usecase/OAuth2AuthenticationUseCase.java +++ b/src/main/java/backend/airo/application/auth/oauth2/usecase/OAuth2AuthenticationUseCase.java @@ -4,6 +4,7 @@ import backend.airo.domain.auth.oauth2.query.FindOAuth2UserQuery; import backend.airo.domain.user.User; import backend.airo.domain.user.enums.ProviderType; +import backend.airo.domain.user.repository.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -34,12 +35,16 @@ public class OAuth2AuthenticationUseCase { @Value("${app.oauth2.failure-url:https://site-navy-six-67.vercel.app/auth/nickname}") private String failureUrl; - public String handleAuthenticationSuccess(OAuth2User oauth2User, String accessToken) { + private String localSuccessBaseUrl = "http://localhost:5173/auth/success"; + + private String localFailureUrl = "http://localhost:5173/auth/nickname"; + + public String handleAuthenticationSuccess(OAuth2User oauth2User, String accessToken, boolean isLocalClient) { // 1. OAuth2 정보 추출 String providerId = oauth2User.getAttribute("provider_id"); ProviderType providerType = oauth2User.getAttribute("provider_type"); - log.info("Provider ID: {}, Provider Type: {}", providerId, providerType); + log.info("Provider ID: {}, Provider Type: {}, Local Client: {}", providerId, providerType, isLocalClient); // 2. 사용자 조회 Optional userOptional = findOAuth2UserQuery.findByProviderIdAndType(providerId, providerType); @@ -51,14 +56,18 @@ public String handleAuthenticationSuccess(OAuth2User oauth2User, String accessTo // 3. 토큰 저장 generateTempCodeCommand.generate(user.getId(), accessToken); - // 4. 성공 URL 반환 - return successBaseUrl + "?token=" + accessToken; + // 4. 환경에 따른 성공 URL 반환 + String baseUrl = isLocalClient ? localSuccessBaseUrl : successBaseUrl; + return baseUrl + "?token=" + accessToken; } else { - log.warn("사용자를 찾을 수 없음 - Provider ID: {}, Provider Type: {}", providerId, providerType); + log.warn("사용자를 찾을 수 없음 - 새로운유저 생성 Provider ID: {}, Provider Type: {}", providerId, providerType); // OAuth2User 속성들을 Redis에 저장 saveOAuth2UserToRedis(accessToken, oauth2User); - return failureUrl + "?token=" + accessToken; + + // 환경에 따른 실패 URL 반환 + String baseUrl = isLocalClient ? localFailureUrl : failureUrl; + return baseUrl + "?token=" + accessToken; } } diff --git a/src/main/java/backend/airo/security/config/SecurityConfig.java b/src/main/java/backend/airo/security/config/SecurityConfig.java index 530e812..58ee417 100644 --- a/src/main/java/backend/airo/security/config/SecurityConfig.java +++ b/src/main/java/backend/airo/security/config/SecurityConfig.java @@ -53,6 +53,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti "/api-docs/**", "/swagger-resources/**", "/webjars/**", + "/api/auth/oauth2/**", "/api/auth/**", "/api/oauth2/**", "/api/login", @@ -72,6 +73,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti ).permitAll() .anyRequest().authenticated() ) + .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .oauth2Login(oauth2 -> oauth2 diff --git a/src/main/java/backend/airo/security/filter/JwtAuthenticationFilter.java b/src/main/java/backend/airo/security/filter/JwtAuthenticationFilter.java index b00fca7..2bfa25a 100644 --- a/src/main/java/backend/airo/security/filter/JwtAuthenticationFilter.java +++ b/src/main/java/backend/airo/security/filter/JwtAuthenticationFilter.java @@ -92,6 +92,9 @@ protected boolean shouldNotFilter(HttpServletRequest request) { path.startsWith("/api/v1/init/") || path.startsWith("/v3/api-docs/") || path.startsWith("/api/login/oauth2/") || + path.startsWith("/api/login") || + path.startsWith("/api/auth/oauth2/") || + path.startsWith("/api/auth/oauth2/authorization/kakao") || path.startsWith("/login/oauth2/"); log.info("필터 실행 여부 체크 - URI: {}, 스킵: {}", path, shouldSkip); diff --git a/src/main/java/backend/airo/security/handler/OAuth2AuthenticationSuccessHandler.java b/src/main/java/backend/airo/security/handler/OAuth2AuthenticationSuccessHandler.java index b6fd109..77587b1 100644 --- a/src/main/java/backend/airo/security/handler/OAuth2AuthenticationSuccessHandler.java +++ b/src/main/java/backend/airo/security/handler/OAuth2AuthenticationSuccessHandler.java @@ -41,8 +41,12 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo OAuth2User oauth2User = (OAuth2User) authentication.getPrincipal(); log.info("OAuth2User 정보: {}", oauth2User.getAttributes()); - // UseCase로 인증 처리 위임 - String redirectUrl = oauth2AuthenticationUseCase.handleAuthenticationSuccess(oauth2User, accessToken); + // 클라이언트 환경 확인 + boolean isLocalClient = isLocalClient(request); + log.info("로컬 클라이언트 여부: {}", isLocalClient); + + // UseCase로 인증 처리 위임 (환경 정보 전달) + String redirectUrl = oauth2AuthenticationUseCase.handleAuthenticationSuccess(oauth2User, accessToken, isLocalClient); log.info("리다이렉트 URL: {}", redirectUrl); response.sendRedirect(redirectUrl); @@ -54,4 +58,26 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo response.sendRedirect("/auth/failure"); } } + + private boolean isLocalClient(HttpServletRequest request) { + // Referer 헤더로 클라이언트 확인 + String referer = request.getHeader("Referer"); + if (referer != null && referer.contains("localhost:5173")) { + return true; + } + + // Origin 헤더로 클라이언트 확인 + String origin = request.getHeader("Origin"); + if (origin != null && origin.contains("localhost:5173")) { + return true; + } + + // Host 헤더로 확인 (프록시 환경 고려) + String host = request.getHeader("Host"); + if (host != null && host.contains("localhost:5173")) { + return true; + } + + return false; + } } \ No newline at end of file