Unsere Lösungen,
ob Start-Up oder etabliertes Unternehmen

Business Process Management

Java

Testautomatisierung

Agile Methoden

Mobile- und Weblösungen

Business-Intelligence

vivir

IT-Sicherheit

Künstliche Intelligenz

viadee Themen

In unserem Blog finden Sie Fachartikel, Success-Stories, News, Messeberichte und vieles mehr... vor allem aber hoffentlich wertvolle Informationen, die Ihnen in Ihrem konkreten Projekt und in Ihrem Business weiterhelfen. Bei Fragen und Anregungen nehmen Sie gerne Kontakt zu uns auf. Bitte benutzen Sie dazu die Kommentarfunktion unter den Blogbeiträgen.

RESTful Batch - sichere und zuverlässige Backend-Verarbeitung mit Spring

17.10.18 11:13

Auch in modernen Web-Anwendungen gibt es sie noch: Langläufer. Komplexe Prozesse, die nicht innerhalb der zumutbaren Online-Antwortzeit ein Ergebnis liefern. Auch diese lassen sich sicher, zuverlässig und stabil in eine moderne Web-Anwendung integrieren, ohne die User-Experience zu stören. Wir zeigen, wie das funktionieren kann.

restful batch

Gibt es in Web-Anwendungen langläufige Prozesse, ist die asynchrone Ausführung obligatorisch, um die reaktive Bedienbarkeit nicht zu beeinträchtigen. Solche Prozesse, wie z. B. die Erstellung eines Massenschreibens mit komplexen Berechnungen, wurden klassisch auf spezifischen Backend-Systemen, oftmals Host-Systemen mit einer eigenen Job-Steuerung und -Überwachung, ausgeführt. Im Rahmen der Umsetzung von vivir®, dem Verwaltungssystem für Versorgungswerke, wollten wir eine solche Lösung vermeiden und einen möglichst leichtgewichtigen Ansatz verfolgen.

Umzusetzen waren diverse langläufige Prozesse in Zahlungsverarbeitung, Meldeverfahren und Massenanschreiben. Für solche Verarbeitungen hat sich Spring Batch als leichtgewichtiges und umfassendes Batch-Framework seit Jahren als De-facto Standard im Java-Umfeld etabliert. Wir haben nun mit einer dedizierten Spring Boot-Anwendung mit REST-Schnittstelle einen generellen Ansatz umgesetzt, welchen wir im Folgenden vorstellen wollen.

Eine Batch-Architektur für Web-Anwendungen

Ausgangssituation war eine Web-Anwendung, von der aus Batch-Prozesse initiiert werden mussten. Diese sollten direkt aus der Anwendung steuerbar sein, ohne den Betrieb der Anwendung zu beeinträchtigen asynchron im Hintergrund laufen und den Usern dennoch eine Rückmeldung zur Ausführung geben können. Das natürlich unter Berücksichtigung der bestehenden Berechtigungs- und Rollenkonzepte.

Um die genannten Anforderungen möglichst leichtgewichtig erfüllen zu können, bot sich eine eigenständige Spring Boot-Anwendung mit einer REST-Schnittstelle als Batch-Backend an. Als solche ist sie flexibel genug, um unabhängig von der Implementierung der Webanwendung genutzt werden zu können. Zum Einsatz kommen hier die Spring Komponenten Spring Boot, Spring Batch und Spring Data Rest sowie Spring Security – aber dazu später mehr.

Das Batch-Modul teilt sich in Konfiguration und Jobs auf, wobei die Konfiguration als Steuerungskomponente fungiert und einen asynchronen Job Launcher aus dem Spring Batch-Framework bereitstellt. Auf diese Weise wird der Aufruf aus der Web-Anwendung „entkoppelt“ und kann im Hintergrund ausgeführt werden. In der zentralen Klasse BatchService werden alle Batch Jobs initiiert und ihre jeweiligen Aufrufe samt Übergabeparameter als public Methoden definiert. Der BatchService, dargestellt in Abbildung 1, fungiert gleichzeitig als RestController. So definiert er das Request Mapping je Batch Job, entnimmt die Job-Parameter aus der URL und startet schließlich den Job. 
@RestController
public class BatchService {

@Autowired
@Qualifier("rentenbezugsmitteilungenJob")
private Job rentenbezugsmitteilungenJob;

@RequestMapping(path = "/jobs/rentenbezugsmitteilungenJob", method = RequestMethod.GET)
public ResponseEntity<HttpStatus> startRentenbezugsmitteilungenJob(
@RequestParam(value = "benutzer") final String benutzer,
@RequestParam(value = "jahr") final Long jahr)
throws Exception {

runJob(rentenbezugsmitteilungenJob, new JobParametersBuilder()
.addString("jobId", LocalDateTime.now().toString())
.addString("benutzer", benutzer)
.addLong("jahr", jahr)
.toJobParameters());
return new ResponseEntity<>(HttpStatus.OK);
}

}

 Abbildung 1 


 

Beispielablauf einer Batch-Verarbeitung

In einer Anwendung zur Kundenverwaltung sollen Weihnachtsanschreiben für alle Kunden erstellt werden. Als Rückmeldung nach Fertigstellung wird eine E-Mail mit dem Speicherpfad der Anschreiben (Druckdateien im pdf-Format) erwartet. 

Beispielablauf einer Batch-Verarbeitung

Abbildung 2 

Ablauf gemäß Abbildung 2:

  1. Aus der Web-Anwendung heraus wird die URL \\localhost\weihnachtsanschreiben?email=benutzer@beispiel.de aufgerufen. Der RestController nimmt den Aufruf entgegen und entnimmt der URL die für die Antwort zu nutzende E-Mail-Adresse.
  2. Innerhalb der aufgerufenen Methode wird zunächst geprüft, ob bereits ein Job Weihnachtsanschreiben aktiv ist.
    1. Ist bereits ein Job aktiv, wird 409 (Conflict) zurückgegeben.
    2. Ist noch kein Job aktiv, wird der Job asynchron gestartet und 200 (OK) zurückgegeben.
  3. Innerhalb der Web-Anwendung kann auf Basis des zurückgelieferten HTTP Statuscodes eine Rückmeldung an den Anwender gegeben werden.
  4. Der Batch Service startet den Batch Job.
  5. Nach Abschluss des Batch Job wird eine E-Mail mit dem Speicherpfad der Anschreiben an die als Übergabeparameter übermittelte Adresse geschrieben.

Batch-abläufe: zuverlässig, stabil und auch sicher? 

Mit dem verfolgten Ansatz haben wir mit Spring Batch und Spring Rest zwei Komponenten zusammengebracht, welche typischerweise nicht gemeinsam eingesetzt werden. Das klappt erstaunlich gut und einfach. So können dem Rest-Controller gleich alle Parameter für den Batch Job mitgeliefert und somit sämtliche Batch Jobs über die REST-Schnittstelle aufgerufen werden. Jedoch können in der bisher vorgestellten Lösung sämtliche Netzwerkteilnehmer Batch Jobs initiieren. Um diese Sicherheitslücke zu schließen, erweitern wir die dedizierte Batch-Anwendung mit dem Spring Security Modul. Dabei greifen wir auf bewährte Methoden zurück und verwenden JSON Web Tokens (JWT), die im Header einer Anfrage mitzusenden sind. Die Implementierung wird im Folgenden umrissen.

Implementierung

Zu implementieren ist ein eigener Request Filter. Die Beispiel-Implementierung (Abbildung 3) zeigt einen erweiterten OncePerRequestFilter (Spring Web Bibliothek).

Innerhalb der Methode doFilterInternal wird dem HttpServletRequest das JWT entnommen und der Anwendungsnutzer mitsamt seinen Berechtigungen ermittelt. Anschließend wird mit den ermittelten Daten ein UsernamePasswordAuthenticationToken (Spring Security Bibliothek) generiert und dem Security Context hinzugefügt.

Der letzte Aufruf filterChain.doFilter(…) erlaubt das Durchlaufen weiterer Filter. Ist nach dem letzten Filter kein Token im Security Context  vorhanden, wird die Anfrage ohne weitere Verarbeitung abgelehnt.

public class CustomFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
String authHeader = request.getHeader("Authorization");
UsernameAndAuthorities userAuth = getUserWithAuthorities(authHeader);

UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
userAuth.getUsername(), null, userAuth.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(token);

filterChain.doFilter(request, response);
}

}

Abbildung 3 

Um einen eigenen Filter zu nutzen, wird, wie in Abbildung 4 zu sehen, ein eigener WebSecurityConfigurerAdapter implementiert. Hier kann der Filter definiert und innerhalb der Methode configure hinzugefügt werden. Für die Prüfung der Autorisierung je Batch-Aufruf werden die einzelnen  

@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

@Override
protected void configure(final HttpSecurity http) throws Exception {
http.addFilterBefore(oncePerRequestFilter(), BasicAuthenticationFilter.class);
}

@Bean
public CustomFilter oncePerRequestFilter() {
return new CustomFilter();
}
}

Abbildung 4 

Service-Aufruf-Methoden innerhalb des RestControllers mit der Annotation @PreAuthorize inkl. der für sie notwendigen Berechtigungen ausgestattet. Diese werden dann bei Aufrufen mit dem Security Context abgeglichen und nur bei vorhandener Berechtigung ausgeführt.

Fazit

Für die Ausführung von Batch-Prozessen innerhalb einer Web-Anwendung lässt sich mit Spring- Bordmitteln eine praktikable und sichere Lösung schaffen. Wie im aufgeführten Beispiel gezeigt wurde, ist der Entwicklungsaufwand überschaubar. Der Ansatz hat sich mit vivir®  bereits in der Praxis behauptet und auch Erweiterungen, wie eine erweiterte Rückmeldung nach Ausführung der Batch Jobs, sind durch die flexibel gestaltete Architektur denkbar.


Autoren: Christopher Brown und Björn Meschede



Jetzt Blog abonnieren!
zum Blog
Christopher Brown

Christopher Brown

Christopher Brown ist seit 2016 bei der viadee IT-Unternehmensberatung. Als Mitglied der Software Schmiede ist er vornehmlich als Software Engineer im Einsatz. Sein Fokus liegt dabei auf dem Design und der Entwicklung von Java-basierten Enterprise Webanwendungen. Besonderen Wert legt er darauf, Clean Code Development als kontinuierlichen Prozess zu verstehen und zu leben.