Add CORS Configure For Spring Boot App

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.

Leave a Comment