diff --git a/src/main/java/org/terning/terningserver/common/config/SecurityConfig.java b/src/main/java/org/terning/terningserver/common/config/SecurityConfig.java index f984b4f..406602a 100644 --- a/src/main/java/org/terning/terningserver/common/config/SecurityConfig.java +++ b/src/main/java/org/terning/terningserver/common/config/SecurityConfig.java @@ -10,8 +10,12 @@ import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.terning.terningserver.common.security.jwt.application.JwtUserIdExtractor; import org.terning.terningserver.common.security.jwt.filter.CustomJwtAuthenticationEntryPoint; import org.terning.terningserver.common.security.jwt.filter.JwtAuthenticationFilter; +import org.terning.terningserver.common.security.ratelimit.RateLimitingService; + +import java.util.List; @Configuration @EnableWebSecurity @@ -19,9 +23,11 @@ @EnableMethodSecurity public class SecurityConfig { - private final JwtAuthenticationFilter jwtAuthenticationFilter; + private final JwtUserIdExtractor jwtUserIdExtractor; + private final RateLimitingService rateLimitingService; private final CustomJwtAuthenticationEntryPoint customJwtAuthenticationEntryPoint; - private static final String[] AUTH_WHITELIST = { + + private static final List AUTH_WHITELIST = List.of( "/v3/api-docs/**", "/swagger-ui.html", "/api/v1/swagger-ui/index.html#/**", @@ -33,10 +39,16 @@ public class SecurityConfig { "/api/v1/push-status", "/api/v1/external/scraps/unsynced", "/api/v1/external/scraps/sync/result" - }; + ); @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter( + jwtUserIdExtractor, + rateLimitingService, + AUTH_WHITELIST + ); + return http .csrf(AbstractHttpConfigurer::disable) .formLogin(AbstractHttpConfigurer::disable) @@ -46,9 +58,9 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .exceptionHandling(exceptionHandling -> exceptionHandling.authenticationEntryPoint(customJwtAuthenticationEntryPoint)) .authorizeHttpRequests(auth -> { - auth.requestMatchers(AUTH_WHITELIST).permitAll(); - auth.anyRequest().authenticated(); - }) + auth.requestMatchers(AUTH_WHITELIST.toArray(new String[0])).permitAll(); + auth.anyRequest().authenticated(); + }) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) .build(); } diff --git a/src/main/java/org/terning/terningserver/common/security/jwt/filter/JwtAuthenticationFilter.java b/src/main/java/org/terning/terningserver/common/security/jwt/filter/JwtAuthenticationFilter.java index 48ef308..954fa62 100644 --- a/src/main/java/org/terning/terningserver/common/security/jwt/filter/JwtAuthenticationFilter.java +++ b/src/main/java/org/terning/terningserver/common/security/jwt/filter/JwtAuthenticationFilter.java @@ -6,11 +6,10 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Component; +import org.springframework.util.AntPathMatcher; import org.springframework.web.filter.OncePerRequestFilter; import org.terning.terningserver.common.security.jwt.application.JwtUserIdExtractor; import org.terning.terningserver.common.security.jwt.auth.UserAuthentication; @@ -19,17 +18,40 @@ import org.terning.terningserver.common.util.IpAddressUtil; import java.io.IOException; +import java.util.List; import java.util.Optional; import static org.springframework.http.HttpHeaders.AUTHORIZATION; -@Component -@RequiredArgsConstructor @Slf4j public class JwtAuthenticationFilter extends OncePerRequestFilter { + private static final AntPathMatcher antPathMatcher = new AntPathMatcher(); + private final JwtUserIdExtractor jwtUserIdExtractor; private final RateLimitingService rateLimitingService; + private final List authWhitelist; + + public JwtAuthenticationFilter( + JwtUserIdExtractor jwtUserIdExtractor, + RateLimitingService rateLimitingService, + List authWhitelist + ) { + this.jwtUserIdExtractor = jwtUserIdExtractor; + this.rateLimitingService = rateLimitingService; + this.authWhitelist = authWhitelist; + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + String requestURI = request.getRequestURI(); + for (String pattern : this.authWhitelist) { + if (antPathMatcher.match(pattern, requestURI)) { + return true; + } + } + return false; + } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)