Die Stiefel zweimal fest geschnürt

Spring Boot 2 – eine Einführung: Spring Data und Spring Security 5

Michael Simons

© Shutterstock / Alexander Raths

 

Spring Data und Spring Security sind zwei wichtige Module, deren Versionen von Spring Boot verwaltet werden und somit durch das Update auf Spring Boot 2 aktualisiert werden. Neue Anwendungen, die direkt mit Spring Boot 2 erstellt werden, profitieren in der Regel. Anwendungen, die aktualisiert werden, müssen einigen dieser Aspekte mit einer aktiven Migration begegnen.

Michael Simons ist Speaker auf der JAX 2018 und Autor des Buches „Spring Boot – Moderne Softwareentwicklung im Spring Ökosystem“, das beim dpunkt.verlag erschienen ist.

1. Spring Data Kay

Das Kay-Release von Spring Data erschien bereits im Oktober 2017 und ist nun Teil des Spring-Boot-2-Dependency-Managements. Aufbauend auf Spring 5, Java 8 und auch den Neuerungen im Java-EE-7-Umfeld beinhaltet es mehrere größere und kleinere Themen:

  • Überarbeites Repository-API
    • Verbesserte Methodennamen und -signaturen, Java-8-Optional
    • Ein neuer Mechanismus zur Komposition von Repositories
  • Eigenständiger Kotlin-Support für unveränderliche (Immutable) Datenklassen
  • Ein Mechanimus, um Nullability-Constraints eines APIs zu deklarieren
  • Und natürlich reaktiver Datenzugriff auf Stores, die ein nicht blockierendes API bieten (u.a. Redis, MongoDB, Couchbase und Cassandra)

Im Folgenden wird das neue API sowie der reaktive Datenzugriff gezeigt. Das neue API wird in einigen Fällen Änderungen im Client-Code bestehender Projekt nach sich ziehen, der reaktive Datenbankzugriff der unterstützten Stores ist eines der Highlights von Spring Data Kay.

1.1. Neue Methodennamen und -signaturen

Die Methodennamen und -signaturen der Spring-Data-Kay-Repositories konnten in einigen Fällen zu Mehrdeutigkeiten führen. Dieser Aspekt war ein treibender Faktor bei der Neugestaltung der Interfaces:

interface CrudRepository<T, ID> extends Repository<T, ID> {

    S save(S entity);

    Iterable<S> saveAll(Iterable<S> entities)

    Optional<T> findById(ID id);

    boolean existsById(ID id);

    Iterable<T> findAllById(Iterable<ID> ids);

    void deleteById(ID id);

    void delete(T entity);

    void deleteAll(Iterable<? extends T> entities);
}

findOne wurde durch findById ersetzt, save und delete mit einem Iterable als Parameter wurden durch explizite …​All-Pendands ersetzt, der Typ der Id muss nicht länger serialisierbar sein.

Die Änderung der Methoden und auch deren Signaturen (Optional<T> anstelle eines möglichen Null-Werts) sind Dinge, die aktiv migriert werden müssen.

1.2. Komponierbare Repositories

Spring Data Repositories können nun aus unterschiedlichen Fragmenten zusammengestellt werden. Die Einschränkung auf eine Basis-Implementierung und eine zusätzliche Implementierung wurde aufgehoben.

@Entity
public class CustomerEntity extends Person {
}

Für die CustomerEntity aus Listing 2 lässt sich nun ein Repository zusammenstellen, das sowohl CRUD-Operationen als auch eigenständige Such- und Bulkoperationen bereitstellt. Listing 3 zeigt das deklarative Interface:

public interface CustomerRepository extends
    CrudRepository<CustomerEntity, Integer>,
    PersonFragment,
    CustomerBulkLoader{
}

Zu den Interfaces gehören natürlich Basis-Implementierungen. Die Namen können weitestgehend frei gewählt werden, die implementierende Klasse sollte auf Impl enden. Listing 4 zeigt dies explizit.

public interface CustomerBulkLoader {
    void loadCustomers(String fileName);
}

public class CustomerBulkLoaderImpl implements CustomerBulkLoader {
    @Override
    public void loadCustomers(String fileName) {
        System.out.println("Loading customers from " + fileName);
    }
}

Damit steht Spring Data Kay einem fachlich getriebenen Schnitt einer Anwendung in keiner Weise mehr im Weg. Obige Beispielen sind Teil der Datenbankbeispiele des Spring-Boot-Buches.

1.3. Reactive-Support

Spring Data Kay unterstützt den nicht blockierenden, „reaktiven“ Zugriff auf eine Vielzahl unterschiedlicher Datastores. Dazu gehören unter anderem Redis, MongoDB, Couchbase und Cassandra, nicht aber JPA. Das JDBC-API ist inhärent blockierend, und die Arbeiten an einer reaktiven Ergänzungen nahmen erst in den letzten Monaten Fahrt auf.

Um reaktive Datentypen, zum Beispiel die Implementierung durch Project Reactor, nutzen zu können, muss entweder ein passender Treiber als Abhängigkeit aufgenommen werden oder, wie Listing 5, der passende Spring-Boot-Starter:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>

Die Repositories können wie gewohnt deklariert werden, Listing 6 zeigt ein einfaches Beispiel sowie die Verwendung in einem reaktiven REST-Controller:

public interface FilmRepository
    extends ReactiveMongoRepository<Film, String> {
}

@RestController
public class FilmRestController {
    private final FilmRepository filmRepository;

    public FilmRestController(FilmRepository filmRepository) {
        this.filmRepository = filmRepository;
    }

    @GetMapping("/api/films")
    public Flux<Film> getAll() {
        return filmRepository
            .findAll(Sort.by("title").ascending());
    }
}

Wird MongoDB als Store genutzt, können infinite streams genutzt werden. Diese basieren auf speziellen Collections in MongoDB, die neue Objekte emittieren, sobald diese in die Collection eingefügt werden. Die Verwendung von @Tailable wird in Listing 7 gezeigt:

public interface FilmWatchedEventRepository  extends
    Repository<FilmWatchedEvent, String> {
    @Tailable
    Flux<FilmWatchedEvent> streamAllBy();
}

2. Fazit

Mit einiger Wahrscheinlichkeit muss Spring-Data-Client-Code nach dem Upgrade einer Spring-Boot-Anwendung ebenfalls angefasst werden, aber das verbesserte API und der Reactive-Support für viele Stores ist der Aufwand wert.

3. Spring Security 5

Spring Security und die automatische Konfiguration von Spring Security durch Spring Boot über den entsprechenden Starter haben wohl die tiefgreifendsten Änderungen erfahren.

3.1. Breaking Changes

3.1.1. Weniger Magie mit Spring Boot

Die automatische Konfiguration von Spring Security durch den spring-boot-starter-security wurde von Grund auf überarbeitet. Traf der Starter in Spring Boot 1.5 noch allerlei Annahmen hinsichtlich der Spring Security Default-Konfiguration, der expliziten Konfiguration der Anwendung und den in Spring Boot „verbauten“ Defaults, so gilt jetzt das Credo: Spring Boot selber hat hinsichtlich Spring Security keine Meinung, es gelten die Defaults von Spring Security. Damit hat zum Beispiel Content-Negotiation Einfluss darüber, ob ein Request Basic- oder Formauthentication angeboten bekommt. Mit diesem Glaubenswechsel einher geht der Verzicht auf Konfigurationseigenschaften unter dem Prefix security: Diese fallen fast alle weg. Übrig bleiben – unter dem neuen Prefix spring.security – Eigenschaften zur Konfiguration eines einzelnen Nutzers, der Reihenfolge der Securityfilter und des neuen OAuth2-Supports.

Mit dem Verzicht auf eine eigene Meinung muss Spring Boot auch nicht mehr versuchen, diese an eine mögliche, vorhandene Konfiguration von Security in der Anwendung anzupassen: Sobald eine Anwendung einen einzelnen Aspekt der Securityfilter-Chain anpasst, obliegt der Anwendung die Konfiguration aller Aspekte.

3.1.2. Ein neuer Mechanismus zur Speicherung von Passwörtern

Die Frage, ob ein Passwort gehashed abgespeichert werden soll oder nicht, stellt sich im Jahr 2018 hoffentlich nicht mehr. Ob ein Hash hingegen sicher ist oder nicht, ist eine Frage, die sich in konstantem Fluss befindet.

Eine Library wie Spring Security kann sich nur selten tiefgreifende Änderungen erlauben und kann daher oftmals nicht schnell genug reagieren, wenn zum Beispiel ein Default-Hashing-Algorithmus geändert werden soll oder nicht. Das Spring Security Team hat sich in Version 5 für einen gründlichen Hausputz entschieden.

In Spring Security 4 war defaultmäßig ein Noop-Password-Encoder konfiguriert, nun werden Passwörter mit BCrypt gehashed. Dabei ist der Default-Encoder kein BCryptPasswordEncoder, sondern ein DelegatingPasswordEncoder. Ein Delegating-Password-Encoder ist in der Lage, verschiedene andere Password-Encoder zu nutzen, sprich verschiedene Formate zu delegieren. Mit diesem Mechanismus einher geht ein neues Format, um Passwörter zu speichern: {id}encodedPasswordid steht dabei für das Kürzel eines Passwort-Encoders und encodedPassword für den Hash selber. Spring Security 5 befreit sich damit von der Kopplung an ein starres Passwortformat und ist in Zukunft für neue Format bereit.

Der neue Passwort-Encoder liest und schreibt dieses Format und kann dabei so konfiguriert werden, dass er gespeicherte Passwörter ohne Kürzel mit einem bestimmten Encoder behandelt.

Zusätzlich zu diesen grundsätzlichen Änderungen wurden alte Password-Encoder, die bereits in Spring Security 4 deprecated waren, gestrichen.

Eine aktive Migration einer Spring-Boot-1-Anwendung zu Spring Boot 2 und Spring Security 5 ist also mindestens in zwei Fällen nötig: Die Anwendung nutzt einen der alten, nun entfernten Password-Encoder, oder die Anwendung nutzt keinen expliziten Passwort-Encoder.

Im ersten Fall kann eventuell ein Message-Digest basierter Encoder helfen, die nach wie vor vorhanden sind. Im zweiten Fall ist eine aktive Migration dringend angeraten, da die Anwendung bis dato ungehashte Passwörter nutzte.

Der Blogpost Spring Security 5: A new password storage format erklärt die Vorteile im Detail und zeigt einige Möglichkeiten zur Migration auf.

3.2. Integration mit dem reaktiven Stack

Eines der zentralen Elemente von Spring Security ist der SecurityContextHolder. Diese Klasse stellt zentral Zugriff auf den gegen eine Anwendung authentifizierten Principal bereit. Im synchronen Web-Stack, bei dem eine Frage vom selben Thread beantwortet wurde, der sie entgegen genommen hat, konnte das über eine ThreadLocal basierte Lösung implementiert werden. Project Reactor — technische Grundlage auch für Reactive Security — bietet mit dem Reactor Context einen unveränderlichen Speicherbereich, der an die Verarbeitungskette eines Flux oder Mono gebunden wird.

Reactive Security funktioniert für den Endentwickler vollständig analog zu Spring Security, sowohl in Hinsicht auf URL- als auch Methodensicherheit. Der Spring Boot Starter spring-boot-starter-security bringt alle notwendigen Abhängigkeiten mit und konfiguriert auch den reaktiven Teil von Sprng Security automatisch.

Natürlich musste auch die Quelle der authentifzierbaren Benutzer reaktiv werden. Parallel zum bekannten UserDetailsServicewurde der ReactiveUserDetailsService eingeführt. Listing 8 zeigt seine Benutzung.

@Bean
public ReactiveUserDetailsService userDetailsService() {
    return new MapReactiveUserDetailsService(
        User.withUsername("visitor")
            .password("{noop}visitor")
            .roles("USER")
            .build(),
        User.withUsername("film")
            .password("{noop}store")
            .roles("STORE")
            .build()
    );
}

Die in Listing 8 definierten Benutzer mit unterschiedlichen Rollen können anschließend eingesetzt werden, um sich an URLs zu binden, die wie in Listing 9 konfiguriert sind.

@Bean
SecurityWebFilterChain springSecurity(
    ServerHttpSecurity http
) {
    return http
        .authorizeExchange()
        .pathMatchers("/api/watchedRightNow")
            .authenticated()
        .pathMatchers("/api/filmWatched")
            .hasRole("STORE")
        .anyExchange().permitAll()
        .and()
        .build();
}

ServerHttpSecurity ist neu in Spring Security 5 und spiegelt zusammen mit seinem alten Pendant, HttpSecurity, den Dualismus der alten, auf der Servlet-Spezifikation basierenden Strukturen und den neuen, reaktiven wider. Viele der bekannten Security-Features können für den reaktiven Stack an dieser Stelle konfiguriert werden.

Genau wie normale Methoden-Sicherheit (@PreAuthorize und Co.) auch in Spring Boot 2 noch bewusst mit @EnableGlobalMethodSecurity konfiguriert wird, so ist das auch für reaktive Methoden der Fall. @nableReactiveMethodSecurity ergänzt den Baukasten.

4. Fazit

Spring Security ist eines der ältesten Projekte im Spring-Biotop. Entstanden 2003 als „Acegi Security“, hat es eine lange Geschichte hinter sich. Ich finde es gut, dass das Team den Mut hat, einen größeren Hausputz anzugehen. Wird diese Library bewusst aktualisiert, ist in der Regel klar, dass eine solche Aktualisierung oft Nacharbeit erfordert. Kommt das Update aber als Teil eines größeren Framework, kann das überraschend sein. Nichtsdestotrotz überwiegen aber auch hier in meinen Augen die Vorteile. Der Verzicht auf „Magie“ mag im Spring-Umfeld vielleicht irritieren, für mich zeigt er vielmehr, dass sich das Spring-Engineering-Team durchaus bewusst ist, dass der Grad zwischen zu wenig und zu viel automatischer Konfiguration schwierig ist.

Im nächsten Teil der Serie zu Spring Boot 2 stelle ich die Änderungen in einem Spring Boot eigenen Modul, dem Spring Boot Actuator vor. Auch dort gibt es die eine oder andere Überraschung.

Spring Boot – Moderne Softwareentwicklung im Spring Ökosystem

Das Spring-Boot-Buch erscheint im Januar 2018 und wird alle aktuellen Themen rund um Spring Boot 2 beinhalten. Dazu gehören neben dem reaktiven Programmiermodell mit Spring 5 auch die neue Actuator-Infrastruktur, der Support von Micrometer.io und vieles mehr.

Dieses Buch soll interessierte Entwickler aus dem Java-EE-Bereich ebenso wie Spring-Entwickler ansprechen und ihnen ein „Rezept“ an die Hand geben, immer wiederkehrende Aufgaben aus dem fachlichen Alltag elegant und ohne Ablenkung mit Spring Boot abzuwickeln.

Weitere Informationenen zum Buch gibt es hier!

Geschrieben von
Michael Simons
Michael Simons
Michael Simons arbeitet als Senior Consultant bei innoQ Deutschland. Er ist Mitglied des NetBeans Dream Teams und Gründer der Euregio JUG. Michael schreibt auf seinem Blog über Java, Spring und Softwarearchitektur. Auf Twitter ist er unterwegs als @rotnroll666, wo er sich unter anderem mit Java, Musik und den kleineren und größeren Problemen als Ehemann und Vater von zwei Kindern beschäftigt. Im Januar 2018 wird Michaels Buch "Spring Boot -- Moderne Softwareentwicklung im Spring-Ökosystem" im dpunkt.verlag erscheinen. Das Buch ist bereits heute unter springbootbuch.de vorbestellbar. Es behandelt Spring Boot 2 und das neue, reaktive Programmierparadigma von Spring 5 ebenso wie Spring-Grundlagen und spricht damit erfahrene Spring-Entwickler wie auch Spring-Neulinge an. Die Schaubilder in diesem Artikel stammen ebenso aus dem Buch, genau wie einige der gezeigten Beispiele. Der Code ist ist bereits jetzt auf GitHub verfügbar.
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: