I have a Spring Security setup where:
JwtAuthFilter is a common authentication filter used across all modules. APIKeyAuthenticationFilter should be executed only in the API Gateway module and must run before JwtAuthFilter.
However, in my API Gateway module, JwtAuthFilter always executes first, and APIKeyAuthenticationFilter is never applied. I need APIKeyAuthenticationFilter to execute first in the API Gateway, and then JwtAuthFilter.
Security Configuration for API Gateway:
@Configuration
@EnableWebSecurity
@Order(-1)
public class APIGatewaySecurityConfiguration {
@Autowired
APIKeyAuthenticationFilter authFilter;
@Bean
@Order(1) // Ensure ordering in security filter chain
public SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationEntryPoint authenticationEntryPoint) throws Exception {
http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/pzp/**").authenticated()
.anyRequest().authenticated()
)
.addFilterBefore(authFilter, UsernamePasswordAuthenticationFilter.class) // API Key filter should execute first
.exceptionHandling(exception -> exception.authenticationEntryPoint(authenticationEntryPoint));
return http.build();
}
}
APIKeyAuthenticationFilter:
@Component
@Order(-1)
public class APIKeyAuthenticationFilter extends OncePerRequestFilter {
@Autowired
APIKeyAuthenticationProvider provider;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String apiKey = request.getHeader("apiKey");
if (apiKey == null || apiKey.isEmpty()) {
filterChain.doFilter(request, response);
return;
}
try {
Authentication authRequest = new APIKeyAuthenticationToken(apiKey);
Authentication authResult = provider.authenticate(authRequest);
SecurityContextHolder.getContext().setAuthentication(authResult);
} catch (AuthenticationException e) {
SecurityContextHolder.clearContext();
}
filterChain.doFilter(request, response);
}
}
Security Configuration for JwtAuthFilter:
@Configuration
@EnableWebSecurity
@Order(99) // Should run after API Gateway security configuration
public class OAuth2SecurityConfiguration {
private final Logger logger = LoggerFactory.getLogger(OAuth2SecurityConfiguration.class);
@Autowired
RemoteTokenServices wSO2TokenServices;
@Autowired
OAuth2AuthenticationManager oAuth2AuthenticationManager;
@Autowired
AuthProperties authProperties;
@Autowired
JwtAuthFilter jwtAuthFilter;
@Autowired
CommonAbstractLogFilter commonAbstractLogFilter;
@PostConstruct
public void done() {
logger.info("Initialization completed for OAuth2SecurityConfiguration");
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
@Bean
@Order(10) // Should execute after APIKeyAuthenticationFilter in API Gateway
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers(getAntPathRequestMatchers()).anonymous()
.requestMatchers(new AntPathRequestMatcher("/**", HttpMethod.OPTIONS.toString())).permitAll()
.requestMatchers(new AntPathRequestMatcher("/pzp/**")).permitAll()
.anyRequest().authenticated()
)
.sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.headers(headers -> headers.addHeaderWriter((request, response) -> {
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.addHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
}))
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) // Should execute after APIKeyAuthenticationFilter
.addFilterAfter(commonAbstractLogFilter, JwtAuthFilter.class)
.build();
}
private AntPathRequestMatcher[] getAntPathRequestMatchers() {
String[] urls = authProperties.getAnonymousURLs();
AntPathRequestMatcher[] requestMatchers = new AntPathRequestMatcher[urls.length + 1];
for (int i = 0; i < urls.length; i++) {
requestMatchers[i] = new AntPathRequestMatcher(urls[i]);
}
requestMatchers[urls.length] = new AntPathRequestMatcher("/api/v1/login");
return requestMatchers;
}
@Bean
public WebSecurityCustomizer webSecurityCustomizer() throws Exception {
String[] urls = authProperties.getAnonymousURLs();
logger.info("Ignored list of resources {} ", authProperties.getAnonymousURLs());
Set<AntPathRequestMatcher> matchers = new HashSet<>();
for (String url : urls) {
matchers.add(new AntPathRequestMatcher(url));
}
return web -> web.ignoring().requestMatchers(matchers.toArray(new AntPathRequestMatcher[0]))
.requestMatchers(new AntPathRequestMatcher("/**", HttpMethod.OPTIONS.toString()));
}
}
JwtAuthFilter (Global Authentication Filter for All Modules):
@Component
@Order(10) // Should execute after APIKeyAuthenticationFilter in API Gateway
public class JwtAuthFilter extends OncePerRequestFilter {
@Autowired
private JwtService jwtService;
@Autowired
AccessTokenConverter accessTokenConverter;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
filterChain.doFilter(request, response);
return;
}
try {
String token = authHeader.substring(7);
Jwt jwt = jwtService.extractJwt(token);
AbstractAuthenticationToken authentication = accessTokenConverter.convert(jwt);
SecurityContextHolder.getContext().setAuthentication(authentication);
} catch (Exception e) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
filterChain.doFilter(request, response);
}
}
Issue: In the API Gateway, JwtAuthFilter is executed before APIKeyAuthenticationFilter, but I need APIKeyAuthenticationFilter to run first. I have already tried setting @Order(-1) for APIKeyAuthenticationFilter and adding it before UsernamePasswordAuthenticationFilter, but it is still skipped.
**Question: How do I ensure that:
APIKeyAuthenticationFilter executes first in API Gateway. JwtAuthFilter executes after APIKeyAuthenticationFilter only in API Gateway. JwtAuthFilter remains the first filter in all other modules. Would appreciate any insights on ordering filters correctly in Spring Boot 3.**