45 Filter

Filter in Spring Boot sind eine Möglichkeit, Anfragen und Antworten zu bearbeiten, bevor sie von einem Controller verarbeitet oder an den Client gesendet werden. Filter bieten eine flexible Möglichkeit, Aspekte wie Authentifizierung, Autorisierung, Logging, Caching, CORS (Cross-Origin Resource Sharing) und vieles mehr in den Request-Response-Zyklus einzubinden.

In diesem Kapitel werden wir erklären, wie Filter in Spring Boot REST Services funktionieren, wie sie erstellt und konfiguriert werden, und wie sie genutzt werden können, um typische Anforderungen wie Authentifizierung und Logging zu implementieren.

45.1 Was sind Filter?

Ein Filter ist eine Komponente, die Anfragen und Antworten auf dem Weg zum und vom Controller abfängt und manipulieren kann. In Spring Boot wird dies durch das Implementieren der javax.servlet.Filter-Schnittstelle ermöglicht. Filter haben keinen direkten Zugriff auf den Controller oder das Geschäftslogik-Schicht, sondern wirken auf die Anfragen, bevor sie den Controller erreichen, oder auf die Antworten, bevor sie den Client erreichen.

45.1.1 Wichtige Anwendungsfälle für Filter:

45.2 Einrichten eines einfachen Filters

Ein Filter wird in Spring Boot durch das Implementieren der Schnittstelle javax.servlet.Filter und die Implementierung der Methode doFilter erstellt. Diese Methode wird für jede Anfrage aufgerufen, die den Filter durchläuft.

45.2.1 Beispiel: Ein einfacher Logging-Filter

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(urlPatterns = "/products/*")
public class LoggingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // Filter initialisieren
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("Anfrage wurde empfangen für: " + request.getRemoteAddr());
        chain.doFilter(request, response); // Anfrage weiterleiten
        System.out.println("Antwort wurde gesendet.");
    }

    @Override
    public void destroy() {
        // Filter aufräumen
    }
}

In diesem Beispiel protokolliert der LoggingFilter alle eingehenden Anfragen und die dazugehörigen Antworten, die den /products-Endpunkt betreffen. Der Filter greift auf die Remote-Adresse des Clients zu, protokolliert sie und leitet dann die Anfrage zur Weiterverarbeitung an die Kette der Filter und Controller weiter.

45.2.2 Registrierung des Filters in Spring Boot

In Spring Boot können Sie Filter auf zwei Arten registrieren: 1. Durch das Verwenden der Annotation @WebFilter wie oben gezeigt. 2. Durch explizite Registrierung in einer FilterRegistrationBean, die Ihnen mehr Kontrolle über die Filterkonfiguration gibt.

45.2.2.1 Beispiel: Registrierung mit FilterRegistrationBean

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.boot.web.servlet.FilterRegistrationBean;

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<LoggingFilter> loggingFilter() {
        FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new LoggingFilter());
        registrationBean.addUrlPatterns("/products/*");
        return registrationBean;
    }
}

In diesem Beispiel wird der LoggingFilter über die FilterRegistrationBean konfiguriert. Sie können den Filter auf bestimmte URL-Muster anwenden und weitere Filtereinstellungen vornehmen.

45.3 Filter für Authentifizierung und Autorisierung

Eine häufige Verwendung von Filtern ist die Implementierung von Authentifizierung und Autorisierung. In diesem Fall prüft der Filter, ob ein Benutzer ein gültiges Token oder eine gültige Anmeldeinformation besitzt, bevor er den Zugriff auf die API-Endpunkte zulässt.

45.3.1 Beispiel: Ein Authentifizierungsfilter mit JWT

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JwtAuthenticationFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        String authHeader = httpRequest.getHeader("Authorization");
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
            return;
        }

        String token = authHeader.substring(7); // Extrahiert den JWT
        // Validierung des JWT (Logik zur Validierung implementieren)
        
        // Falls gültig, weiter zur nächsten Komponente in der Filterkette
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
    }
}

In diesem Beispiel prüft der JwtAuthenticationFilter, ob die Anfrage ein JWT im Authorization-Header enthält. Wenn der JWT-Token nicht vorhanden oder ungültig ist, wird eine 401 Unauthorized-Antwort zurückgegeben.

45.4 Filter-Reihenfolge steuern

In Spring Boot können Sie die Reihenfolge festlegen, in der Filter angewendet werden. Dies ist besonders wichtig, wenn Sie mehrere Filter haben, die in einer bestimmten Reihenfolge ausgeführt werden sollen.

45.4.1 Beispiel: Festlegen der Filter-Reihenfolge

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<LoggingFilter> loggingFilter() {
        FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new LoggingFilter());
        registrationBean.addUrlPatterns("/products/*");
        registrationBean.setOrder(1); // Reihenfolge festlegen (niedrigere Werte werden zuerst ausgeführt)
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean<JwtAuthenticationFilter> jwtFilter() {
        FilterRegistrationBean<JwtAuthenticationFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new JwtAuthenticationFilter());
        registrationBean.addUrlPatterns("/products/*");
        registrationBean.setOrder(2); // Wird nach dem LoggingFilter ausgeführt
        return registrationBean;
    }
}

In diesem Beispiel wird der LoggingFilter zuerst ausgeführt, gefolgt vom JwtAuthenticationFilter, da der LoggingFilter die niedrigere Priorität hat (Order 1).

45.5 CORS-Konfiguration mit Filtern

CORS (Cross-Origin Resource Sharing) ist ein Mechanismus, der den Zugriff von verschiedenen Domains auf Ihre API steuert. Filter sind eine gute Möglichkeit, CORS in Spring Boot zu konfigurieren.

45.5.1 Beispiel: CORS-Filter

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CorsFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setHeader("Access-Control-Allow-Origin", "*");
        httpResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
        httpResponse.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");

        if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) request).getMethod())) {
            httpResponse.setStatus(HttpServletResponse.SC_OK);
            return;
        }

        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
    }
}

In diesem Beispiel setzt der CorsFilter die entsprechenden CORS-Header, um den Zugriff von externen Domains zu ermöglichen. Der Filter fängt auch OPTIONS-Anfragen ab, die bei CORS-Vorabprüfungen verwendet werden.

45.6 tl;dr

Filter in Spring Boot bieten eine leistungsstarke Möglichkeit, Anfragen und Antworten auf niedriger Ebene zu bearbeiten, bevor sie den Controller erreichen. Sie eignen sich hervorragend für Aufgaben wie Authentifizierung, Logging, CORS und vieles mehr. Durch die einfache Implementierung und Konfiguration können Sie Filter flexibel an Ihre Anforderungen anpassen, um eine sichere und skalierbare RESTful Webservice-Architektur zu schaffen.