Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -0,0 +1,41 @@
package com.Timo.Timo.global.auth.filter;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

@Component

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

p3) @component 필터를 addFilterBefore에도 추가하면 컨테이너와 Spring Security가 각각 등록할 수 있습니다. Spring Security도 이 경우 이중 실행 또는 예상과 다른 순서가 발생할 수 있다고 명시합니다. Spring Security 공식 문서
아래처럼 MdcLoggingFilter와 동일하게 전역 등록을 비활성화해야 합니다.

@Bean
FilterRegistrationBean<OriginValidationFilter> originValidationFilterRegistration(
    OriginValidationFilter filter
) {
  var registration = new FilterRegistrationBean<>(filter);
  registration.setEnabled(false);
  return registration;
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

안 그래도 관련해서 코드래빗 리뷰를 받아서 해당 부분 반영하여 푸시하였었습니당

public class OriginValidationFilter extends OncePerRequestFilter {

private static final List<String> PROTECTED_PATHS = List.of(
"/api/v1/auth/reissue",
"/api/v1/auth/logout",
"/api/v1/auth/withdraw"
);

@Value("${app.oauth2.allowed-frontend-urls}")
private List<String> allowedFrontendUrls;

@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain
) throws ServletException, IOException {
String path = request.getRequestURI();

if (PROTECTED_PATHS.stream().anyMatch(path::equals)) {
String origin = request.getHeader("Origin");
if (origin == null || !allowedFrontendUrls.contains(origin)) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
}

filterChain.doFilter(request, response);
}
}
22 changes: 16 additions & 6 deletions src/main/java/com/Timo/Timo/global/auth/utils/CookieUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,32 @@
public class CookieUtil {

public static ResponseCookie createCookie(String name, String value, long maxAgeSeconds, boolean secure) {
return ResponseCookie.from(name, value)
ResponseCookie.ResponseCookieBuilder builder = ResponseCookie.from(name, value)
.httpOnly(true)
.secure(secure)
.path("/api/v1/auth")
.maxAge(Duration.ofSeconds(maxAgeSeconds))
.sameSite("Strict")
.build();
.sameSite(secure ? "None" : "Strict");

if (secure) {
builder.partitioned(true);
}

return builder.build();
}

public static ResponseCookie expireCookie(String name, boolean secure) {
return ResponseCookie.from(name, "")
ResponseCookie.ResponseCookieBuilder builder = ResponseCookie.from(name, "")
.httpOnly(true)
.secure(secure)
.path("/api/v1/auth")
.maxAge(0)
.sameSite("Strict")
.build();
.sameSite(secure ? "None" : "Strict");

if (secure) {
builder.partitioned(true);
}

return builder.build();
}
}
15 changes: 14 additions & 1 deletion src/main/java/com/Timo/Timo/global/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.Timo.Timo.global.config;

import com.Timo.Timo.global.auth.filter.OriginValidationFilter;
import com.Timo.Timo.global.auth.handler.JwtAuthenticationEntryPoint;
import com.Timo.Timo.global.auth.handler.OAuthFailureHandler;
import com.Timo.Timo.global.auth.filter.OAuthOriginCaptureFilter;
Expand Down Expand Up @@ -32,6 +33,7 @@ public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
private final CorsConfigurationSource corsConfigurationSource;
private final OAuthOriginCaptureFilter oAuthOriginCaptureFilter;
private final OriginValidationFilter originValidationFilter;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
Expand Down Expand Up @@ -69,7 +71,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(mdcLoggingFilter(), JwtAuthenticationFilter.class)
.addFilterBefore(oAuthOriginCaptureFilter, OAuth2AuthorizationRequestRedirectFilter.class);
.addFilterBefore(oAuthOriginCaptureFilter, OAuth2AuthorizationRequestRedirectFilter.class)
.addFilterBefore(originValidationFilter, JwtAuthenticationFilter.class);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

return http.build();
}
Expand All @@ -87,4 +90,14 @@ public FilterRegistrationBean<MdcLoggingFilter> mdcLoggingFilterRegistration(
registrationBean.setEnabled(false);
return registrationBean;
}

@Bean
public FilterRegistrationBean<OriginValidationFilter> originValidationFilterRegistration(
OriginValidationFilter originValidationFilter
) {
FilterRegistrationBean<OriginValidationFilter> registrationBean =
new FilterRegistrationBean<>(originValidationFilter);
registrationBean.setEnabled(false);
return registrationBean;
}
}