Nutzungsvorgaben HttpInvoker
Diese Seite ist ein Teil der IsyFact-Standards. Alle Inhalte der Seite, insbesondere Texte und Grafiken, sind urheberrechtlich geschützt. Alle urheberrechtlichen Nutzungs- und Verwertungsrechte liegen beim Bundesverwaltungsamt.
Die Nutzung ist unter den Lizenzbedingungen der Creative Commons Namensnennung 4.0 International gestattet.
Java Bibliothek / IT-System
Name |
Art |
Version |
|
Bibliothek |
v3.2.x |
|
Bibliothek |
v3.2.x |
|
Bibliothek |
v3.2.x |
1. Einleitung
Dieses Dokument ist aus mehreren Quellen entstanden und fasst die vorhandene Dokumentation zum Thema HTTP Invoker zusammen. Der Baustein HTTP Invoker steht unter Bestandsschutz und wird nicht mehr aktiv weiterentwickelt. Er wird in einem der nächsten Major Releases aus der IsyFact entfernt. |
Das Dokument KonzeptHttpInvoker konzentriert sich auf die konzeptionellen Aspekte von Services mit HTTP Invoker.
Dieses Dokument beschreibt, wie diese Services konkret umgesetzt werden. Es richtet sich an Entwickler, die ein IT-System gemäß den Vorgaben der IsyFact mit der Fähigkeit zur Kommunikation mit anderen IT-Systemen ausstatten müssen und beschreibt, was bei der Umsetzung zu bedenken ist.
Dazu beschreibt das Kapitel Maven-Konfiguration zunächst die grundlegende Konfiguration, um den Baustein in ein IT-System einzubinden. Danach beschreibt das Dokument die Realisierung eines Services in den Kapiteln:
Danach geht es um die Paketierung einer Service-Schnittstelle und die Nutzung einer Service-Schnittstelle durch ein anderes IT-System.
2. Maven-Konfiguration
Zur Verwendung des Bausteins HTTP Invoker genügt es, die folgenden zwei Bibliotheken aus der IsyFact als Maven-Abhängigkeiten einzubinden.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.bund.bva.isyfact</groupId>
<artifactId>isy-serviceapi-core</artifactId>
</dependency>
<dependency>
<groupId>de.bund.bva.isyfact</groupId>
<artifactId>isy-serviceapi-sst</artifactId>
</dependency>
</dependencies>
</dependencyManagement>
3. Realisierung der Service-Schnittstelle
Die folgenden Abschnitte führen durch die Realisierung einer Service-Schnittstelle mit HTTP Invoker. Sie behandeln die folgenden Bestandteile: das Remote-Bean, die Exception-Fassade, die Service-Fassade und schließlich die Transportobjekte. Die letzten Abschnitte beschreiben dann die Integration anderer Bausteine zur Realisierung querschnittlicher Aspekte: Fehlerbehandlung, Logging und die Absicherung von Service-Methoden.
Wichtige ergänzende Hinweise sowie eine sehr umfangreiche Anleitung zur Erstellung von Service-Schnittstellen mit HTTP Invoker bietet das IsyFact Tutorial. |
3.1. Realisierung des Remote-Beans
Das Remote-Bean wird als Java-Schnittstelle realisiert.
Jede Methode entspricht dabei einem möglichen Service-Aufruf.
Jede Methode der RemoteBean-Schnittstelle muss als ersten Parameter ein Objekt der Klasse AufrufKontextTo
bzw. ClientAufrufKontextTo
verwenden.
Dieser Parameter dient dazu, Meta-Informationen zum jeweiligen Aufruf zu übergeben.
Daneben enthält die Schnittstelle natürlich noch weitere, fachliche Parameter, die frei definiert werden können.
Die Verwendung von Parametern in einer Schnittstelle ist im folgenden Beispiel dargestellt:
public class BeispielRemoteBean {
public BeispielHolenAntwortTo holeBeispielAnfrage(
AufrufKontextTo kontext, BeispielHolenAnfrageTo anfrage)
throws BeispielTechnicalToException;
}
Im Folgenden werden die beiden Klassen AufrufKontextTo
und ClientAufrufKontextTo
näher beschrieben.
AufrufKontextTo: Die Klasse AufrufKontextTo
wird für HTTP Invoker Schnittstellen verwendet, die durch IT-Systeme definiert werden.
Die Klasse kapselt die Informationen, mit denen andere IT-Systeme aufgerufen wurde:
-
Behörde: Das Behördenkennzeichen der aufrufenden Behörde,
-
Kennung: Die Kennung des aufrufenden Benutzers oder des aufrufenden Fremdprogramms,
-
Kennwort: Das Passwort des aufrufenden Benutzers oder des aufrufenden Fremdprogramms,
-
Rollen: Die Rollen des aufrufenden Benutzers oder des aufrufenden Fremdprogramms,
-
Korrelations-ID: Die ID, um den Service-Aufruf eindeutig zu identifizieren.
ClientAufrufKontextTo: Die Klasse ClientAufrufKontextTo
wird für HTTP Invoker Schnittstellen verwendet, die durch Service-Consumer definiert werden.
Im Gegensatz zu AufrufKontextTo
kapselt diese Klasse die Informationen, um sich bei einem externen Service zu authentifizieren und zu autorisieren:
-
Kennung: Die Kennung mit welcher der externe Service aufgerufen wird,
-
Kennwort: Das Passwort mit welchem der externe Service aufgerufen wird,
-
Zertifikat: Das Zertifikat, um sich beim externen Service zu authentifizieren,
-
Zertifikat-Kennwort: Das Passwort des Zertifikats für die Authentifizierung.
Sowohl AufrufKontextTo
als auch ClientAufrufKontextTo
sind in der Bibliothek isy-serviceapi-sst
definiert.
3.2. Realisierung der Exception-Fassade
Die Exception-Fassade implementiert das Remote-Bean-Interface und definiert in jeder Methode einen try
-catch
-Block, der alle Fehler des Anwendungskerns abfängt und in Fehler der Service-Schnittstelle umwandelt.
In Listing 3 ist ein Beispiel für eine Exception-Fassade eines IT-Systems angegeben.
Die Service-Operationen sind in diesem Fall die Methoden des Interfaces BeispielRemoteBean
.
Konkret handelt es sich lediglich um die Service-Operation holeBeispielAnfrage
.
Die Service-Operation ist mit der Annotation @StelltLoggingKontextBereit
versehen, die eine mit dem AufrufKontext
übergebene Korrelations-ID im Logging-Kontext registriert und diesen beim Verlassen der Methode wieder aufräumt.
Falls im AufrufKontext keine Korrelations-ID vorhanden ist, so erzeugt die Annotation eine neue Korrelations-ID. |
Es ist wichtig den Logging-Kontext zu setzen, bevor die Exception-Fassade aktiv wird.
Die Implementierung der Service-Operation reicht den Methodenaufruf an die implementierende Klasse (BeispielService
) weiter, fängt auftretende Fehler jedoch über einen try
-catch
-Block ab.
Der try
-catch
-Block unterscheidet zwischen Exceptions der Datenbankzugriffsschicht (DataAccessException
) und allen anderen Exceptions (Throwable
), um einen passenden Fehlertext in die Log-Dateien zu schreiben.
public class BeispielExceptionFassade implements BeispielRemoteBean
{
private static final IsyLoggerStandard logger = ...;
private BeispielServiceFassade beispielService;
...
@StelltLoggingKontextBereit
public BeispielHolenAntwortTo holeBeispielAnfrage
(AufrufKontextTo kontext, BeispielHolenAnfrageTo anfrage)
throws BeispielTechnicalToException {
try {
return beispielService.holeBeispielAnfrage(kontext,anfrage);
} catch (DataAccessException e) {
logger.error("Fehler bei Transaktion", e);
throw new BeispielTechnicalToException(...);
} catch (Throwable t) {
logger.error("...", t);
throw new BeispielTechnicalToException(...);
}
}
...
}
3.3. Realisierung der Service-Fassade
Die Service-Fassade übernimmt die restlichen Aufgaben der Service-Logik, d.h. Transformation der Transportobjekte in Objekte des Anwendungskerns und umgekehrt, sowie die Autorisierung des Aufrufs.
In Listing 4 ist ein Beispiel für eine Service-Fassade angegeben.
Die Implementierung der Service-Fassade erfolgt analog zur Implementierung der Exception-Fassade.
Die nach außen angebotene Service-Operation (holeBeispielAnfrage
) wird jedoch nicht eins-zu-eins an die implementierende Klasse weitergeleitet, da sich die Parameter und der Rückgabewert des Aufrufs unterscheiden.
Nach außen hin werden Transportobjekte angeboten.
Intern arbeitet die Anwendung mit ihren eigenen Entitäten.
Diese können sich von den nach außen hin angebotenen Transportobjekten unterscheiden, z.B. weil sie zusätzliche Attribute enthalten, einzelne Attribute anders benennen oder die Daten in irgendeiner Form anders repräsentieren als die Transportobjekte.
In der Service-Fassade erfolgt auch die Autorisierung eines Zugriffs auf eine Service-Methode.
Voraussetzung für die Autorisierung ist die Auswertung des mitgelieferten AufrufKontextes über die Annotation @StelltAufrufKontextBereit
aus der Bibliothek isy-serviceapi-core
an der Service-Methode.
Anschließend kann über die Annotation @Gesichert
der Bibliothek isy-sicherheit
die Berechtigung zum Zugriff auf die Methode geprüft werden.
Hier werden alle benötigten Rechte des Aufrufers überprüft.
Alternativ kann die Annotation @Gesichert
auch an der Service-Klasse verwendet werden, wenn alle Methoden die gleiche Autorisierung erfordern.
Das Mapping im Beispiel wird durch einen Bean Mapper umgesetzt.
Vor dem Aufruf werden die Parameter gemappt (Klasse BeispielHolenAnfrageTo
auf Klasse BeispielHolenAnfrage
), nach dem Aufruf der Rückgabewert (Klasse BeispielHolenAntwort
auf Klasse BeispielHolenAntwortTo
).
Die Komponente Service-Logik wird durch eine entsprechende Spring-Konfigurationsklasse verschaltet.
public class BeispielServiceFassade {
private static final IsyLoggerStandard logger = ...;
private MapperFacade beanMapper;
private Beispiel beispiel;
@StelltAufrufKontextBereit
@Gesichert(Rechte.RECHT_ZUGRIFFBEISPIEL)
public BeispielHolenAntwortTo holeBeispielAnfrage(
AufrufKontextTo kontext, BeispielHolenAnfrageTo anfrage) {
try {
BeispielHolenAnfrage anfrageAwk = beanMapper.map(anfrage, BeispielHolenAnfrage.class);
BeispielHolenAntwort antwortAwk = beispiel.holeBeispielAnfrage(anfrageAwk);
return beanMapper.map(antwortAwk, BeispielHolenAntwortTo.class);
} catch (MappingException e) {
logger.error("...", e);
throw new TechnicalException(...);
}
...
}
3.4. Realisierung von Transportobjekten
Transportobjekte dürfen keine externen Abhängigkeiten haben, da sie Teil der ausgelieferten Schnittstelle sind. Bei Transportobjekten ist zu beachten, dass die UID stets 0 ist (s. Listing 5).
public class BeispielTransportobjekt {
private static final long serialVersionUID = 0L;
}
3.5. Fehlerbehandlung
HTTP Invoker Schnittstellen besitzen, wie bereits beschrieben, eigene Exceptions, die nur zur Kommunikation mit anderen IT-Systemen dienen. Für diese Transport-Exceptions gilt über die Vorgaben des Konzept Fehlerbehandlung hinaus noch:
-
Sie erben immer von
BusinessToException
oderTechnicalToException
und implementieren somit immerSerializable
. -
Sie stellen die Felder Ausnahme-ID, UUID und Fehlernachricht zur Verfügung.
-
Sie erben nicht von internen Exceptions des IT-Systems.
Daraus ergibt sich für Transport-Exceptions folgende Hierarchie:
Weiterhin gelten pro Service-Methode folgende Vorgaben:
Definition von technischen Exceptions: Service-Methoden deklarieren keine oder eine technische Exception. Die technische Exception muss für alle Service-Methoden einer Service-Schnittstelle gleich sein.
Definition von fachlichen Exceptions: Service-Methoden können beliebig viele fachliche Exceptions deklarieren. Diese können spezifisch für jede Service-Methode sein.
Übermittlung von Daten: Die Felder Ausnahme-ID, UUID und Fehlernachricht müssen stets übertragen werden. Weiterhin darf kein Stack-Trace übertragen werden.
Die Fehlerbehandlung geschieht in der Exception-Fassade, die einen Service-Aufruf nach außen hin kapselt (siehe Abbildung 2).
Die Exception-Fassade bildet die Klammer um einen Aufruf an die Anwendung und ist für die Top-Level Fehlerbehandlung zuständig. Sie leitet den Aufruf an die Service-Fassade weiter, die wiederum den Anwendungskern aufruft. Dieser zweistufige Prozess ist notwendig, falls es unerwartete Exceptions in der Service-Fassade selbst gibt (z.B. falls diese die Transaktionsklammer um mehrere Aufrufe des Anwendungskerns bildet). Solche unerwartete Exceptions treten außerhalb der eigentlichen Anwendung auf und könnten die Fehlerbehandlung an der Schnittstelle selbst stören. Daher liegt die Exception-Fassade noch vor der Service-Fassade, um auch diese Fehler abzufangen, zu loggen, in Transport-Exceptions umzuwandeln und an den Aufrufer weiterzureichen.
Listing 6 zeigt die Fehlerbehandlung in der Exception-Fassade für das Beispiel-Remote-Bean.
public class BeispielExceptionFassade implements BeispielRemoteBean {
private static final IsyLoggerStandard LOG = ...;
private final BeispielServiceFassade service;
...
@StelltLoggingKontextBereit
public BeispielHolenAntwortTo holeBeispielAnfrage(
AufrufKontextTo kontext, BeispielHolenAnfrageTo anfrage) {
throws BeispielBusinessToException, BeispielTechnicalToException {
try {
return service.holeBeispielAnfrage(kontext, anfrage);
} catch (InternalBusinessException ex) {
LOG.debug("...", ex);
// Exceptions in Schnittstellen-Exceptions transformieren.
throw (BeispielBusinessToException)ExceptionMapper.mapException(
ex, BeispielBusinessToException.class);
} catch (InternalTechnicalRuntimeException ex) {
LOG.error("...", ex);
throw (BeispielTechnicalToException)ExceptionMapper.mapException(
ex, BeispielTechnicalToException.class);
} catch (Throwable t) {
LOG.error("...", t);
// Unbekannte Exceptions in Schnittstellen-Exceptions transformieren.
BeispielTechnicalToException ex = ExceptionMapper.createToException(
AusnahmeIdUtil.getAusnahmeId(t),
new FehlertextProviderImpl(),
BeispielTechnicalToException.class);
LOG.error("Übergebener Fehler: " + ex.getMessage());
throw ex;
}
}
Das Code-Beispiel in Listing 6 fängt alle Exceptions und wandelt diese in entsprechende Transport-Exceptions um.
Als erwartete Exceptions gibt es hier die Exception InternalBusinessException
.
Diese wird, sofern sie auftritt, in eine BeispielBusinessToException
umgewandelt und weitergereicht.
Zu beachten ist, dass in das Error-Log nur betrieblich relevante Fehler geschrieben werden sollen.
Fachliche Fehler sind in der Regel irrelevant für den Betrieb.
Daher wird die |
Weitere erwartete Fehler gibt es nicht, somit wird nun eine Fehlerbehandlung für unerwartete Fehler der Anwendung durchgeführt (alle Exceptions vom Typ InternalTechnicalRuntimeException
).
Als letzte mögliche Fehlerbehandlung werden alle unerwarteten Exceptions vom Typ Throwable
gefangen.
Der erste Block in diesem Beispiel behandelt eine fachliche Exception. Die restlichen Blöcke behandeln unerwartete, technische Exceptions. Fachliche Exceptions müssen immer in fachliche Transport-Exceptions umgewandelt werden, alle anderen Exceptions sind in technische Transport-Exceptions umzuwandeln.
Alle Blöcke einer solchen Fassade auf der Anwendungsgrenze verwenden die Klasse ExceptionMapper
(siehe Mapping von Exceptions) zur Umwandlung der fachlichen und technischen Exceptions in Transport-Exceptions und zur Erstellung von Transport-Exceptions.
Letzteres wird im letzten catch
-Block des obigen Code-Beispiels genutzt, da in diesem Fall keine gemäß Konzept Fehlerbehandlung aufgebaute Exception und somit weder Ausnahme-ID, UUID noch Fehlernachricht vorhanden sind.
In diesem Fall ist die benötigte Ausnahme-ID zu berechnen, mithilfe der Schnittstelle AusnahmeIdErmittler
(siehe Mapping von Exceptions).
Die catch
-Blöcke für interne Runtime-Exceptions (hier vom Typ InternalTechnicalRuntimeException
) und alle übrigen unerwarteten Exceptions (Throwable
) müssen immer implementiert werden.
Hierdurch wird verhindert, dass die Schnittstelle nicht spezifizierte Exceptions weiterreicht.
3.5.1. Mapping von Exceptions
Zur Umwandlung von internen Exceptions in Transport-Exceptions stellt die Bibliothek isy-serviceapi-core
eine eigene Klasse zur Verfügung: ExceptionMapper
(siehe Abbildung 3).
Die Klasse ExceptionMapper
bietet zwei statische Methoden an, um aus fachlichen oder technischen Exceptions entsprechende Transport-Exceptions zu erstellen.
Hierfür muss lediglich die umzuwandelnde Exception und die Klasse der gewünschten Transport-Exception mitgegeben werden.
Listing 7 zeigt ein Beispiel für die Umwandlung einer technischen Exception in eine technische Transport-Exception.
ExceptionMapper.mapException(ex, BeispielTechnicalToException.class)
Es können jedoch weitere Exceptions auftreten, die nicht gemäß Konzept Fehlerbehandlung aufgebaut sind. Diese besitzen keine Ausnahme-ID oder eine UUID, z.B. Runtime-Exceptions aus Frameworks von Drittherstellern. Auch diese Exceptions müssen in Transport-Exceptions umgewandelt werden. Listing 8 zeigt ein Beispiel für eine solche Umwandlung.
Throwable t = ...;
AusnahmeIdErmittler a = ...;
BeispielTechnicalToException ex = ExceptionMapper.createToException(
a.ermittleAusnahmeId(t),
new FehlertextProviderImpl(),
BeispielTechnicalToException.class);
Dazu muss die Schnittstelle AusnahmeIdErmittler
implementiert werden.
Sie bietet eine Methode, String getAusnahmeId(Throwable)
, zur Analyse einer übergebenen Exception und zur Rückgabe der zur Exception passenden Ausnahme-ID.
Diese Klasse ist anwendungsspezifisch und für jede Anwendung zu implementieren.
Listing 9 zeigt eine mögliche Implementierung für das Mapping von Exceptions auf Ausnahme-IDs.
@Bean
public class BeispielAusnahmeIdErmittler implements AusnahmeIdErmittler {
public String ermittleAusnahmeId(Throwable e) {
if (throwable instanceof DataAccessException) {
// generische Datenbank-Fehlermeldung
return FehlerSchluessel.MSG_GENERISCHER_DB_FEHLER;
} else if (throwable instanceof JmxException) {
// generische JMX-Fehlermeldung
return FehlerSchluessel.MSG_GENERISCHER_JMX_FEHLER;
} else if (throwable instanceof InternalBusinessException) {
// Bei Exceptions mit Ausnahme-ID: diese auslesen
return ((InternalBusinessException) throwable).getAusnahmeID();
}
// Kein Mapping Möglich: generische Fehlermeldung
return FehlerSchluessel.MSG_GENERISCHER_FEHLER;
}
}
Die Ermittlung der Ausnahme-ID sollte auch auf die internen Exceptions der Anwendung prüfen, auch wenn es nie zu einer positiven Prüfung dieser Bedingungen kommen sollte.
Sollte hier also ein Treffer für interne Exceptions auftreten, so wurden die catch
-Blöcke nicht sauber implementiert (z.B. wurde einfach nur catch Throwable
verwendet).
Dies würde dazu führen, dass die Original-Nachricht überschrieben würde, was besonders bei fachlichen Exceptions zu einem Informationsverlust für den Aufrufer führt.
3.6. Absicherung von Service-Methoden
In HTTP Invoker Schnittstellen werden in der Regel einzelne Methoden der Service-Fassade (hinter der Exception-Fassade) abgesichert.
Zur Autorisierung von Aufrufen sind Informationen aus dem Aufrufkontext nötig.
Dieser wird als erster Schnittstellenparameter in Form eines Transportobjekts mit jedem Aufruf übergeben.
Dieses Transportobjekt, eine Instanz der Klasse AufrufKontextTo
, muss die Informationen zum anfragenden Anwender und dessen Rollen enthalten.
Zur Autorisierung muss die Service-Methode oder die Service-Fassade mit @StelltAufrufKontextBereit
annotiert sein.
Die Annotation signalisiert einem Interceptor, das Transportobjekt auszulesen und die bereitgestellten Informationen in den Aufrufkontext des IT-Systems zu übertragen.
Mit dieser Voraussetzung können Service-Methoden durch die Annotation @Gesichert
abgesichert werden.
Als Standard-Parameter werden alle Rechte aufgelistet, die eine Anfrage für einen Aufruf beinhalten muss.
Die Rechte sind immer konjunktiv verknüpft.
Eine disjunktive Verknüpfung von Rechten ist nicht möglich.
Folgendes Beispiel (Listing 10) zeigt die Implementierung einer Service-Methode, für die der Aufrufkontext automatisch ausgelesen und anschließend die Autorisierung gegen den Baustein Sicherheit durchgeführt wird.
@StelltAufrufKontextBereit
@Gesichert("RechtA", "RechtB")
public void abgesicherteMethode(AufrufKontextTo kontext, ...) {
...
}
Erfüllt ein Aufrufer die Forderungen der Annotation @Gesichert
nicht, so wird ein Fehler des Typs AutorisierungFehlgeschlagenException
erzeugt.
Der Fehler wird in der Regel nicht lokal behandelt, sondern einfach zurückgegeben.
Letztlich deuten fehlende Rechte meist auf einen unberechtigten Zugriff oder einen Fehler in der Konfiguration einer aufrufenden Anwendung hin, sodass hier die normale Fehlerbehandlung greift.
3.7. Kompatibilität zu OAuth 2
Aufgrund der Umstellung auf OAuth 2 zur Authentifizierung und Autorisierung muss das Bearer Token bei jedem Schnittstellenaufruf übertragen werden. Dies geschieht für existierende Schnittstellen transparent.
Der Zugriff auf das Bearer Token geschieht über den AufrufKontextVerwalter
.
Die Klasse enthält hierfür ein Attribut bearerToken
.
public interface AufrufKontextVerwalter<T extends AufrufKontext> {
String getBearerToken();
void setBearerToken(String bearerToken);
}
Zur Übertragung wird der HTTP-Header Authorization
verwendet.
4. Paketierung einer Service-Schnittstelle
IT-Systeme teilen sich folgende Java-Klassen der HTTP Invoker Schnittstelle:
-
Java-Interface der Schnittstelle (Remote-Bean),
-
Java-Klassen der Transportobjekte,
-
Java-Klassen der Transport-Exceptions.
Diese Klassen müssen als JAR mit einer einzigen Abhängigkeit auf die Bibliothek isy-serviceapi-sst
paketiert werden.
Sie werden anhand der Vorgaben in Detailkonzept Komponente Service und IsyFact Versionierung versioniert und anhand der Vorgaben in IsyFact Namenskonventionen benannt.
5. Bereitstellung einer Service-Schnittstelle
In der Konfigurationsklasse der Service-Schicht wird die HTTP Invoker Konfiguration der Service-Schnittstelle eingebunden. Dazu werden das Remote-Bean-Interface und die zugehörige Implementierung in Form der Exception-Fassade als Service konfiguriert Listing 12. Der Bean-Name ist für die URL, unter welcher der Service erreichbar sein wird, wichtig.
@Configuration
public class ServiceConfiguration {
@Bean("/BeispielBean_v1_0")
public HttpInvokerServiceExporter beispielService(BeispielExceptionFassade beispiel) {
HttpInvokerServiceExporter exporter = new HttpInvokerServiceExporter();
exporter.setService(beispiel);
exporter.setServiceInterface(BeispielRemoteBean.class);
return exporter;
}
}
6. Nutzung einer Service-Schnittstelle
Zur Nutzung einer entfernten Schnittstelle bindet ein IT-System das JAR der Schnittstelle via Maven ein und initialisiert die Remote-Beans damit.
Das geschieht über die vom Spring Framework bereitgestellte Factory-Klasse HttpInvokerProxyFactoryBean
, wie in Listing 13 dargestellt.
Auf dieser Bean können dann die entfernten Methoden aufgerufen werden.
@Configuration
public class ServiceConfiguration {
@Bean
public HttpInvokerProxyFactoryBean beispielRemoteBean(HttpInvokerRequestExecutor executor, ServiceConfigProperties config) {
HttpInvokerProxyFactoryBean invoker = new HttpInvokerProxyFactoryBean();
invoker.setServiceUrl(config.getServiceUrl());
invoker.setServiceInterface(BeispielRemoteBean.class);
invoker.setHttpInvokerRequestExecutor(executor);
return invoker;
}
@Bean
public TimeoutWiederholungHttpInvokerRequestExecutor executor(ServiceConfigProperties config) {
TimeoutWiederholungHttpInvokerRequestExecutor executor = new TimeoutWiederholungHttpInvokerRequestExecutor();
executor.setAnzahlWiederholungen(config.getWiederholungen());
executor.setTimeout(config.getTimeout());
return executor;
}
}
Die FactoryBean erwartet eine Service-URL und ein Remote-Bean-Interface zur Initialisierung. Der Host-Teil der URL muss in jedem Fall in der betrieblichen Konfiguration der Anwendung zu finden sein. Das Remote-Bean-Interface ist im Schnittstellen-JAR verfügbar.
Die Nutzung des hier im Beispiel verwendeten TimeoutWiederholungHttpInvokerRequestExecutor
ist zwar optional, wird aber dringend empfohlen, um Langläufer zu vermeiden.
Dieser Executor bricht nach dem angegebenen Timeout die Anfrage ab und wiederholt sie bis zur maximalen angegebenen Wiederholungsanzahl.
Die Parameter für Timeout und Wiederholungen sind Bestandteil der betrieblichen Konfiguration (s. Konzept Konfiguration).
Nutzt das IT-System den Baustein Logging, muss statt der Spring-eigenen Factory die erweiterte IsyHttpInvokerProxyFactoryBean
genutzt werden.
Sie versieht die Remote-Beans automatisch mit einem LogMethodInterceptor
, der die Aufrufzeiten der ausgehenden Aufrufe misst und loggt.
Die Konfiguration erfolgt wie in Listing 14 gezeigt.
@Configuration
public class ServiceConfiguration {
@Bean
public IsyHttpInvokerProxyFactoryBean beispielRemoteBean(HttpInvokerRequestExecutor executor, ServiceConfigProperties config) {
IsyHttpInvokerProxyFactoryBean invoker = new IsyHttpInvokerProxyFactoryBean();
invoker.setServiceUrl(config.getServiceUrl());
invoker.setServiceInterface(BeispielRemoteBean.class);
invoker.setHttpInvokerRequestExecutor(executor);
invoker.setRemoteSystemName(config.getRemoteSystemName());
return invoker;
}
}
Die erweiterte FactoryBean erwartet nur einen zusätzlichen Parameter remoteSystemName
.
Dieser wird genutzt, um in allen Log-Ausgaben einen sprechenden Systemnamen zu setzen.
6.1. Kompatibilität zu IF1/IF2/IF3
6.1.1. IF1- und IF2-Client
Anwendungen basierend auf der IF1 oder IF2 können HTTP-Invoker-Aufrufe wie gewohnt durchführen, da diese einen gefüllten AufrufKontext
besitzen.
Hier bleibt das Vorgehen wie bisher in der Anleitung beschrieben.
6.1.2. IF3-Client
Im Zuge des Upgrades zu IF3 wird der AufrufKontext
(deprecated) nicht mehr verwendet und der von IF3-Anwendungen verwendete Sicherheitsbaustein isy-security
wird nativ keine HTTP-Invoker Schnittstellen mehr unterstützen.
Um weiterhin die Kompatibilität von HTTP-Invoker-Aufrufen gegenüber IF1-, IF2- und IF3-Anwendungen zu gewährleisten, wird bei einem HTTP-Invoker-Aufruf ein AufrufKontext
als Transportobjekt ad hoc erzeugt.
Das AufrufKontextTo
wird dabei direkt aus dem Spring SecurityContext
bzw. dem AccessToken
erzeugt.
Dazu wird der Berechtigungsmanager
des Bausteins isy-security
verwendet; isy-security
ist als eine provided
-Dependency im isy-serviceapi-core
-Baustein eingebunden, d.h. für IF1 und IF2-Anwendungen wird diese Dependency nicht benötigt, da diese Anwendungen bereits einen gefüllten AufrufKontext
vorhalten und für eine IF3-Anwendung muss diese Dependency zur Runtime zur Verfügung stehen, wenn ein Transportobjekt des AufrufKontext
ad hoc erzeugt werden soll.
In der IF3-Applikation muss somit isy-security eingebunden sein, um ein AufrufKontextTo
zu erzeugen.
Zur Erzeugung des AufrufKontextTo
wird eine RemoteInvocationFactory
eingesetzt, welche beim Aufruf einer Servicemethode durch den Parameter null
(statt einem AufrufKontextTo Objekt) signalisiert bekommt, dass eine Erstellung notwendig ist.
@Autowired
TestService testServiceIsy;
[...]
// Parameter "null" -> AufrufKontextTo wird ad-hoc aus SecurityContext erstellt
testServiceIsy.holeDenText(null);
Per Default wird die RemoteInvocationFactory
nur in IsyHttpInvokerProxyFactoryBean
verwendet.
Falls die Spring-eigene Factory verwendet wird, muss die RemoteInvocationFactory
explizit in einem Setter (setRemoteInvocationFactory()
) gesetzt werden.
7. Sicherheit
Bei der Übertragung von Daten über eine HTTP-Invoker-Schnittstelle werden Java-Klassen serialisiert und deserialisiert. Wird bei der Deserialisierung die Integrität der Daten nicht sichergestellt, können Angreifer schadhaften Code über eine HTTP-Invoker-Schnittstelle einschleusen (siehe CWE-502). Das Betrifft alle HTTP-Invoker-Schnittstellen, die direkt von außerhalb einer Anwendungslandschaft benutzt werden können.
Um die Sicherheit an dieser Stelle zu erhöhen, müssen HTTP-Invoker-Schnittstellen den IsyHttpInvokerServiceExporter
verwenden.
Dieser Exporter deaktiviert standardmäßig die Verwendung von Proxy-Klassen, über welche potenziell schadhafter Code deserialisiert werden kann.