package com.example.demo.config;
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.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Arrays;
import java.util.List;
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins(
"http://localhost:3000",
"https://myapp.com"
)
.allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")
.allowedHeaders("*")
.exposedHeaders("Authorization", "X-Total-Count")
.allowCredentials(true)
.maxAge(3600);
}
// Alternative: Bean-based configuration for Spring Security integration
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList(
"http://localhost:3000",
"https://myapp.com"
));
configuration.setAllowedMethods(Arrays.asList(
"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"
));
configuration.setAllowedHeaders(List.of("*"));
configuration.setExposedHeaders(Arrays.asList(
"Authorization",
"X-Total-Count"
));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", configuration);
return source;
}
}
package com.example.demo.controller;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
@CrossOrigin(
origins = {"http://localhost:3000", "https://myapp.com"},
methods = {RequestMethod.GET, RequestMethod.POST},
allowedHeaders = "*",
exposedHeaders = {"X-Total-Count"},
allowCredentials = "true",
maxAge = 3600
)
public class UserController {
@GetMapping
public ResponseEntity<List<User>> getUsers() {
// Method inherits CORS configuration from class level
return ResponseEntity.ok(users);
}
// Override CORS for specific endpoint
@CrossOrigin(origins = "*")
@GetMapping("/public")
public ResponseEntity<String> publicEndpoint() {
return ResponseEntity.ok("Public data");
}
}
CORS (Cross-Origin Resource Sharing) controls which domains can access APIs. Browsers enforce same-origin policy by default. I configure allowed origins, methods, headers, and credentials. @CrossOrigin enables CORS per controller or method. Global configuration via WebMvcConfigurer applies to entire application. Preflight requests (OPTIONS) verify permissions before actual requests. Credentials require allowCredentials and specific origins—wildcards don't work. Exposed headers make custom headers accessible to JavaScript. Max age caches preflight responses. Proper CORS prevents security issues while enabling legitimate cross-domain communication. Production environments restrict allowed origins to known frontends. Development often allows all origins for convenience.