Eğitim Portalı/Spring Boot/Spring Boot CORS Desteği
Spring Boot03-spring-boot/13-cors

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 (allowedOriginPatterns ile 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 gerekir
Ornek1.java
1// 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}
canlı çalıştırma için ortam gerekir

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.