Eğitim Portalı/Spring Boot/Spring Boot Servlet Filter
Spring Boot03-spring-boot/12-servlet-filter

Spring Boot Servlet Filter

Filter, Java web dünyasının (Servlet API'si) en eski ara katman mekanizmasıdır (topic 65). Bir isteği, daha Spring MVC'ye (DispatcherServlet) ulaşmadan en dışta karşılar ve yanıt çıkarken son olarak görür. Spring Boot…

Spring Boot Servlet Filter

Filter, Java web dünyasının (Servlet API'si) en eski ara katman mekanizmasıdır (topic 65). Bir isteği, daha Spring MVC'ye (DispatcherServlet) ulaşmadan en dışta karşılar ve yanıt çıkarken son olarak görür. Spring Boot, filter'ları kolayca kaydetmeni sağlar. Bu konu, filter'ları ve interceptor'dan (topic 11) ne zaman ayrıldığını ele alır.

Filter: doFilter ve zincir

Bir Filter, tek metodu doFilter ile isteği sarmalar ve zinciri ilerletir:

class IzlemeFilter implements Filter {
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
        // ÖNCE: istek henüz işlenmedi
        chain.doFilter(req, res);   // zinciri ilerlet (sonraki filter -> DispatcherServlet -> controller)
        // SONRA: yanıt hazır
    }
}

chain.doFilter(...) çağrısının öncesi isteğe, sonrası yanıta karşılık gelir. Çağırmazsan istek hiç işlenmez (isteği burada kesebilirsin). Birden çok filter bir zincir oluşturur.

Örnek 1 (./Ornek1.java) her isteğe bir izleme kimliği (trace id) başlığı ekleyen ve süreyi loglayan bir filter kurar; self-test bu başlığı yanıtta okur.

Filter'ı kaydetmek

Spring Boot'ta birkaç yol var:

// 1) FilterRegistrationBean — yol/sıra üzerinde tam kontrol (önerilen)
@Bean FilterRegistrationBean<IzlemeFilter> f() {
    var reg = new FilterRegistrationBean<>(new IzlemeFilter());
    reg.addUrlPatterns("/*");
    reg.setOrder(1);
    return reg;
}

// 2) @Component olarak işaretle — otomatik tüm yollara kaydedilir (basit ama kontrol az)
@Component class IzlemeFilter implements Filter { ... }

FilterRegistrationBean, hangi yollara (addUrlPatterns) ve hangi sırayla (setOrder) uygulanacağını belirlemeni sağlar.

Filter vs Interceptor

İkisi de "araya girer" ama farklı katmanlarda:

Filter (Servlet)Interceptor (Spring MVC)
SeviyeKonteyner — en dıştaDispatcherServlet sonrası (MVC içinde)
KapsamHer şey (statik dosyalar, hata sayfaları dahil)Yalnızca controller'a giden istekler
ErişimHam ServletRequest/ResponseHandler (controller metodu), model
TipikCORS, sıkıştırma, kimlik, kodlama, trace idMVC yetki, model ekleme, süre ölçme

Sıra: Filter → DispatcherServlet → Interceptor → Controller (ve dönüşte ters). Genel/düşük seviyeli işler filter'a, MVC-bilgisi gereken işler interceptor'a gider.

Tipik kullanım alanları

  • Trace id / korelasyon kimliği ekleme (dağıtık loglama).
  • CORS (genelde Spring'in CORS desteği veya bir CorsFilter ile — sonraki konu).
  • Sıkıştırma, karakter kodlama, güvenlik başlıkları.
  • Kimlik doğrulama (Spring Security büyük ölçüde filter zincirine dayanır!).

Not: Spring Security tamamen bir filter zinciri üzerine kuruludur (topic 04). Yani her güvenli istek, sen yazmasan da bir dizi filter'dan geçer.

Özet

Servlet Filter'ın isteği en dışta sarmalayan ara katman olduğunu; doFilter + zincir mantığını, Spring Boot'ta FilterRegistrationBean ile kaydı (Örnek 1); Filter↔Interceptor farkını (seviye, kapsam, erişim) ve tipik kullanımları (trace id, CORS, güvenlik) öğrendik. Sırada, tarayıcı güvenliğinin önemli bir konusu: CORS desteği.

Kod Örnekleri(1)

Ornek1

ortam gerekir
Ornek1.java
1// Ornek1: Servlet Filter — servlet konteyneri seviyesinde her isteği sarmalamak.
2// Çalıştırma: portal gömülü Tomcat ile başlatır, self-test çıktısını alır.
3package com.egitim.springboot.filter;
4
5import jakarta.servlet.*;
6import jakarta.servlet.http.HttpServletRequest;
7import jakarta.servlet.http.HttpServletResponse;
8import org.springframework.boot.CommandLineRunner;
9import org.springframework.boot.SpringApplication;
10import org.springframework.boot.autoconfigure.SpringBootApplication;
11import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
12import org.springframework.boot.web.servlet.FilterRegistrationBean;
13import org.springframework.context.annotation.Bean;
14import org.springframework.web.bind.annotation.*;
15import org.springframework.web.client.RestClient;
16
17import java.io.IOException;
18import java.util.UUID;
19
20@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
21public class Ornek1 {
22
23    public static void main(String[] args) { SpringApplication.run(Ornek1.class, args); }
24
25    // Filter: en dışta, TÜM isteklerde (statik dosyalar dahil) çalışır; ham request/response görür.
26    static class IzlemeFilter implements Filter {
27        @Override
28        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
29                throws IOException, ServletException {
30            HttpServletRequest http = (HttpServletRequest) req;
31            HttpServletResponse httpRes = (HttpServletResponse) res;
32
33            // Her isteğe bir izleme kimliği (trace id) ekle — log korelasyonu için.
34            String izlemeId = UUID.randomUUID().toString().substring(0, 8);
35            httpRes.setHeader("X-Izleme-Id", izlemeId);
36            long t0 = System.currentTimeMillis();
37            System.out.println("  [filter:önce] " + http.getMethod() + " " + http.getRequestURI() + " izlemeId=" + izlemeId);
38
39            chain.doFilter(req, res); // zinciri ilerlet -> isteğin geri kalanı (interceptor, controller) çalışır
40
41            System.out.println("  [filter:sonra] durum=" + httpRes.getStatus()
42                    + " süre=" + (System.currentTimeMillis() - t0) + "ms");
43        }
44    }
45
46    // Filter'ı kaydet + hangi yollara/sırayla uygulanacağını belirle.
47    @Bean
48    FilterRegistrationBean<IzlemeFilter> izlemeFilter() {
49        FilterRegistrationBean<IzlemeFilter> reg = new FilterRegistrationBean<>(new IzlemeFilter());
50        reg.addUrlPatterns("/*");   // tüm yollar
51        reg.setOrder(1);            // birden çok filter varsa sıra
52        return reg;
53    }
54
55    @RestController
56    static class DemoController {
57        @GetMapping("/selam") String selam() { return "merhaba"; }
58    }
59
60    @Bean
61    CommandLineRunner selfTest() {
62        return args -> {
63            RestClient c = RestClient.create("http://localhost:8080");
64            System.out.println("\n========= SERVLET FILTER SELF-TEST =========");
65            var yanit = c.get().uri("/selam").retrieve().toEntity(String.class);
66            System.out.println("yanıt gövdesi: " + yanit.getBody());
67            System.out.println("Filter'ın eklediği başlık X-Izleme-Id: " + yanit.getHeaders().getFirst("X-Izleme-Id"));
68            System.out.println("(Filter her istekte zincirden önce/sonra çalıştı ve başlık ekledi.)");
69            System.out.println("============================================");
70        };
71    }
72}
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.