CORS Configuration in Spring Boot: A Complete Guide

Cross-Origin Resource Sharing (CORS) is a crucial security mechanism that allows or restricts resources requested from one domain to another domain. In Spring Boot applications, proper CORS configuration is essential for frontend applications to communicate with your backend API.

What is CORS?

CORS is a browser security feature that blocks requests from one origin to another unless the server explicitly allows it. This becomes important when your frontend (e.g., React app on http://localhost:3000) needs to access your backend API (e.g., Spring Boot on http://localhost:8080).

CORS Configuration Methods in Spring Boot

Method 1: Global CORS Configuration

Create a global CORS configuration that applies to all endpoints in your application.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.util.Arrays;
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
// Allow credentials
config.setAllowCredentials(true);
// Allow specific origins
config.setAllowedOriginPatterns(Arrays.asList(
"http://localhost:3000",
"http://localhost:4200",
"https://mydomain.com"
));
// Allow specific headers
config.setAllowedHeaders(Arrays.asList(
"Origin",
"Content-Type",
"Accept",
"Authorization",
"X-Requested-With"
));
// Allow specific methods
config.setAllowedMethods(Arrays.asList(
"GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"
));
// Expose specific headers to the client
config.setExposedHeaders(Arrays.asList(
"Authorization",
"Content-Disposition"
));
// How long the response to preflight request can be cached (in seconds)
config.setMaxAge(3600L);
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}

Method 2: Using WebMvcConfigurer (for Spring MVC)

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOriginPatterns("http://localhost:3000", "https://*.mydomain.com")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
registry.addMapping("/public/**")
.allowedOriginPatterns("*")
.allowedMethods("GET")
.allowedHeaders("*")
.maxAge(1800);
}
}

Method 3: Using WebFluxConfigurer (for Spring WebFlux)

import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;
@Configuration
public class WebFluxConfig implements WebFluxConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("*")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}

Method 4: Controller-Level CORS Configuration

Apply CORS configuration to specific controllers or methods.

import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "http://localhost:3000", 
maxAge = 3600,
allowCredentials = "true")
public class UserController {
@GetMapping
@CrossOrigin(origins = {"http://localhost:3000", "https://admin.mydomain.com"})
public List<User> getUsers() {
// Implementation
return userService.getAllUsers();
}
@PostMapping
public User createUser(@RequestBody User user) {
// Implementation
return userService.createUser(user);
}
}

Method 5: Method-Level CORS with @CrossOrigin

@RestController
@RequestMapping("/api/products")
public class ProductController {
@GetMapping
@CrossOrigin(origins = "*") // Allow all origins for this endpoint
public List<Product> getAllProducts() {
return productService.getAllProducts();
}
@PostMapping
@CrossOrigin(origins = "http://localhost:3000", 
methods = RequestMethod.POST,
allowedHeaders = {"Content-Type", "Authorization"})
public Product createProduct(@RequestBody Product product) {
return productService.createProduct(product);
}
}

Application Properties Configuration

You can also configure CORS through application.properties or application.yml:

application.properties

# CORS configuration
app.cors.allowed-origins=http://localhost:3000,https://mydomain.com
app.cors.allowed-methods=GET,POST,PUT,DELETE,OPTIONS
app.cors.allowed-headers=*
app.cors.allow-credentials=true
app.cors.max-age=3600

application.yml

app:
cors:
allowed-origins: 
- "http://localhost:3000"
- "https://mydomain.com"
allowed-methods: GET,POST,PUT,DELETE,OPTIONS
allowed-headers: "*"
allow-credentials: true
max-age: 3600

Security-Specific CORS Configuration

When using Spring Security, configure CORS through security configuration:

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfigurationSource;
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(csrf -> csrf.disable()) // Often disabled for APIs
.authorizeHttpRequests(authz -> authz
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
);
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
// Return your CorsConfigurationSource implementation
return new UrlBasedCorsConfigurationSource();
}
}

Testing CORS Configuration

Create a test to verify your CORS configuration:

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
public class CorsTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testCorsHeaders() throws Exception {
mockMvc.perform(options("/api/users")
.header("Origin", "http://localhost:3000")
.header("Access-Control-Request-Method", "GET"))
.andExpect(status().isOk())
.andExpect(header().exists("Access-Control-Allow-Origin"))
.andExpect(header().string("Access-Control-Allow-Origin", "http://localhost:3000"));
}
}

Common CORS Issues and Solutions

Issue 1: Preflight Request Failing

Solution: Ensure OPTIONS method is allowed and proper headers are set.

Issue 2: Credentials Not Working

Solution: Set allowCredentials(true) and use specific origins (not wildcard *).

Issue 3: Headers Not Exposed

Solution: Use setExposedHeaders() to expose custom headers to the client.

Issue 4: Multiple CORS Configurations

Solution: Be aware that method-level configurations override class-level, which override global configurations.

Best Practices

  1. Be Specific with Origins: Avoid using * for origins when credentials are required.
  2. Limit Exposed Headers: Only expose necessary headers to enhance security.
  3. Use Appropriate Max Age: Balance between performance and security.
  4. Environment-Specific Configuration: Use different CORS settings for development, testing, and production.
  5. Test Thoroughly: Verify CORS behavior across different browsers and scenarios.

This comprehensive CORS configuration guide ensures your Spring Boot application can securely handle cross-origin requests while maintaining proper security standards.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper