Table of Contents
Overview
When connecting from frontend to backend, you may encounter the infamous CORS error
Access to XMLHttpRequest at 'http://localhost:9080/api/video-jobs' from origin 'http://localhost:5173' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'http://localhost:5172' that is not equal to the supplied origin.
I will not explain to you what CORS error is (there are plenty of good resource for that). I only show you how to configure a filter in Spring Boot so you can fix this error and go back to work on your awesome app.
Let’s get started.
Create a CORS filter
First of all, you need to create a CORS filter to return the necessary headers saying that your backend allows certain methods and origins.
package com.datmt.video.config.filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpFilter; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.io.IOException; @Component public class CorsFilter extends HttpFilter { @Value("${app.cors.allowed-origins:}") private String allowedOrigins; @Override protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { response.setHeader("Access-Control-Allow-Origin", allowedOrigins); response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type"); if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } else { chain.doFilter(request, response); } } }
This essentially create a bean that I will inject later in the security config. Basically, here I pass the list of allowedOrigins from the application.properties (application.yaml). For every http requests to my server, I add the Access-Control
headers. You may want to adjust accordingly to your need.
You also need to configure the list of URLs you want to allow in the origins header. Make sure the URLs don’t have the trailing slash
app: cors: allowed-origins: ${CORS_ALLOWED_ORIGINS:http://localhost:5173}
Configure the filter in your Security config
In your Configuration bean where you configure the SecurityFilterChain, add the corsFilter bean and make it run before your authentication filter
private final CorsFilter corsFilter; @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> { auth .requestMatchers( "/api/public/**", "/api/users/authenticate", "/swagger-ui/**", "/api/swagger-ui/**", "/swagger-ui.html", "/api-docs/**", "/v3/api-docs/**", "/hooks/**" ).permitAll() .requestMatchers("/api/admin/**") .hasRole("admin") .anyRequest().authenticated(); }) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .addFilterBefore(corsFilter, BearerTokenAuthenticationFilter.class) .csrf(AbstractHttpConfigurer::disable) .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())); return http.build(); }
Here, I add the corsFilter before the BearerTokenAuthenticationFilter filter so I can return the necssary Access-Control
header before jwt verification. If the BearerTokenAuthenticationFilter runs first, the OPTIONS requests would return 401 instead of 200.
Conclusion
That’s all! Now you can start working on your next billion $ app.
I build softwares that solve problems. I also love writing/documenting things I learn/want to learn.