Spring Boot CORS Desteği
Modern web uygulamalarında frontend (örn. https://uygulamam.com) ve backend API (örn. https://api.uygulamam.com) genelde farklı kökenlerde (origin) çalışır. Tarayıcılar güvenlik gereği, bir sayfanın kendi kökeni dışın…
Spring Boot CORS Desteği
Modern web uygulamalarında frontend (örn. https://uygulamam.com) ve backend API (örn.
https://api.uygulamam.com) genelde farklı kökenlerde (origin) çalışır. Tarayıcılar güvenlik
gereği, bir sayfanın kendi kökeni dışındaki bir API'ye JavaScript ile istek atmasını
varsayılan olarak engeller (Same-Origin Policy). CORS (Cross-Origin Resource Sharing), sunucunun
"şu kökenlere izin veriyorum" demesini sağlayan mekanizmadır. Bu konu, Spring Boot'ta CORS'u nasıl
yapılandıracağını ele alır. (Bu portalın kendisi de React frontend :5173 → backend :8085 için
CORS kullanır.)
CORS nasıl çalışır?
Tarayıcı çapraz-köken bir istek atarken Origin başlığı ekler. Sunucu, izin veriyorsa yanıta
Access-Control-Allow-Origin başlığını koyar; tarayıcı bu başlığı görüp yanıtı JavaScript'e
verir. Karmaşık istekler için tarayıcı önce bir preflight (OPTIONS) isteği gönderir ve
izinli metot/başlıkları sorar.
Önemli: CORS bir tarayıcı mekanizmasıdır. Sunucudan sunucuya veya Postman/curl isteklerinde geçerli değildir (oralarda Same-Origin Policy yoktur). Yani CORS bir "güvenlik duvarı" değil, tarayıcıya "bu kökene izin var" diyen bir işarettir.
Global CORS yapılandırması
Tüm uygulama için merkezi yapılandırma — WebMvcConfigurer.addCorsMappings:
@Bean WebMvcConfigurer corsConfig() { return new WebMvcConfigurer() { public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("https://uygulamam.com") // izinli köken(ler) .allowedMethods("GET", "POST", "PUT", "DELETE") .allowedHeaders("*") .allowCredentials(true); // çerez/kimlik gönderimi } }; }
Tekil CORS: @CrossOrigin
Belirli bir controller/metot için yerel CORS:
@RestController @CrossOrigin(origins = "https://uygulamam.com") class ApiController { ... }
Örnek 1 (./Ornek1.java) hem global yapılandırmayı hem @CrossOrigin'i kurar; self-test, Origin
başlığı göndererek yanıttaki Access-Control-Allow-Origin başlığını okur.
Güvenlik uyarıları
allowedOrigins("*")dikkat: Herkese açmak, kötü amaçlı sitelerin API'ne tarayıcıdan istek atmasına izin verir. Üretimde kesin köken listesi ver.allowCredentials(true)+*birlikte olmaz: Kimlik (çerez/Authorization) gönderiliyorsa joker köken yasaktır; kökenleri açıkça belirtmelisin (allowedOriginPatternsile desen verilebilir).- Spring Security ile: Security devredeyse CORS'u Security yapılandırmasında da etkinleştirmen
gerekir (
http.cors(...)), aksi halde filter zinciri isteği CORS'tan önce reddedebilir.
Yapılandırma öncelikleri
- Tüm API aynı politikayı paylaşıyorsa → global (
WebMvcConfigurer). - Yalnızca birkaç uç farklıysa →
@CrossOrigin. - İkisi birlikte kullanılabilir; tekil ayar ilgili uçta geçerli olur.
Özet
CORS'un tarayıcının çapraz-köken güvenlik politikası (Same-Origin) için sunucunun verdiği izin
mekanizması olduğunu; Access-Control-Allow-Origin akışını; global (WebMvcConfigurer) ve tekil
(@CrossOrigin) yapılandırmayı (Örnek 1) ve güvenlik tuzaklarını (* + credentials, Security ile
entegrasyon) öğrendik. Bu, frontend-backend ayrı çalışan her modern uygulamada gereklidir. Bununla
Spring Boot derinleştirmesinin ilk batch'i (web ara katmanları) tamamlandı.
▶ Kod Örnekleri(1)
Ornek1
ortam gerekir1// Ornek1: CORS desteği — tarayıcının çapraz-köken (cross-origin) isteklerine izin.
2// Çalıştırma: portal gömülü Tomcat ile başlatır, self-test çıktısını alır.
3package com.egitim.springboot.cors;
4
5import org.springframework.boot.CommandLineRunner;
6import org.springframework.boot.SpringApplication;
7import org.springframework.boot.autoconfigure.SpringBootApplication;
8import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
9import org.springframework.context.annotation.Bean;
10import org.springframework.web.bind.annotation.*;
11import org.springframework.web.client.RestClient;
12import org.springframework.web.servlet.config.annotation.CorsRegistry;
13import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
14
15@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
16public class Ornek1 {
17
18 public static void main(String[] args) { SpringApplication.run(Ornek1.class, args); }
19
20 // GLOBAL CORS yapılandırması: hangi köken/metot/başlıklara izin verilir.
21 @Bean
22 WebMvcConfigurer corsConfig() {
23 return new WebMvcConfigurer() {
24 @Override public void addCorsMappings(CorsRegistry registry) {
25 registry.addMapping("/api/**")
26 .allowedOrigins("https://uygulamam.com") // izinli köken(ler)
27 .allowedMethods("GET", "POST")
28 .allowedHeaders("*");
29 }
30 };
31 }
32
33 @RestController
34 @RequestMapping("/api")
35 static class GlobalController {
36 @GetMapping("/veri") String veri() { return "global cors verisi"; }
37 }
38
39 // Tek bir controller/metot için @CrossOrigin ile yerel CORS (global'den bağımsız).
40 @RestController
41 @RequestMapping("/acik")
42 @CrossOrigin(origins = "*") // bu controller herkese açık (dikkatli kullan!)
43 static class AcikController {
44 @GetMapping("/veri") String veri() { return "açık cors verisi"; }
45 }
46
47 @Bean
48 CommandLineRunner selfTest() {
49 return args -> {
50 RestClient c = RestClient.create("http://localhost:8080");
51 System.out.println("\n========= CORS SELF-TEST =========");
52
53 // İzinli köken: yanıtta Access-Control-Allow-Origin görünür.
54 var izinli = c.get().uri("/api/veri").header("Origin", "https://uygulamam.com")
55 .retrieve().toEntity(String.class);
56 System.out.println("İzinli köken -> Allow-Origin: "
57 + izinli.getHeaders().getFirst("Access-Control-Allow-Origin"));
58
59 // @CrossOrigin(*) controller: her köken için Allow-Origin döner.
60 var acik = c.get().uri("/acik/veri").header("Origin", "https://baska-site.com")
61 .retrieve().toEntity(String.class);
62 System.out.println("@CrossOrigin(*) -> Allow-Origin: "
63 + acik.getHeaders().getFirst("Access-Control-Allow-Origin"));
64 System.out.println("==================================");
65 };
66 }
67}Bu örnek Spring / Spring Boot (Gradle) ortamı gerektirir; tek dosya olarak java Ornek1.java ile çalışmaz. Orijinal portal bunu gömülü Tomcat / Spring context ile koşuyordu. Beklenen davranış yukarıdaki anlatım ve kodda açıklanmıştır.