Übertragung von Authentisierungsinformationen im JWT
- Daniela Gehle
- Matthias Thiele
Dieser Artikel beschreibt, wie im jadice web toolkit Authentisierungsinformationen, z.B. in Form von Tokens, übertragen werden können.
Anforderung
In Enterprise-Umgebungen kommt der Authentisierung von Benutzern beim Dokumentenzugriff eine zentrale Bedeutung bei. Moderne Lösungen basieren häufig auf der Erzeugung bzw. Validierung von (in der Regel kurzlebigen) Tokens, siehe z.B. JSON Web Tokens bzw. OAuth.
Aufgrund der in der Regel kurzen Lebensdauer der Tokens müssen diese bei jedem Request vom jadice web toolkit Client an den Server übermittelt werden. Serverseitig werden die Tokens entweder direkt validiert oder an einen Backend-Service weitergeleitet, so dass dieser eine Validierung durchführen kann.
Wichtig ist, dass dem jadice web toolkit Server jederzeit ein gültiges Token zur Verfügung steht, damit auch im Falle eines Dokument-Recovery-Vorgangs ein neuerliches Abfragen des Dokuments am Archiv authentisiert ist. Wir benötigen also eine Möglichkeit, Tokens wiederholt vom Client an den Server zu übertragen.
Übertragung von Tokens vom Client an den Server
Es wird vorausgesetzt, dass clientseitig im Integrations-Code bereits ein Token am Identity Provider angefordert wurde.
Dieses Token wird dem jadice web toolkit gesetzt und daraufhin im Hintergrund an den jadice web toolkit Server übertragen - und zwar vollkommen unabhängig vom eingestellten Transportverfahren (also egal ob Longpoll, Websocket oder Server Sent Events). Da das jadice web toolkit verschiedene Transportverfahren unterstützt, funktioniert natürlich eine Übertragung im HTTP Header nicht, wie sie z.B. für JSON Web Tokens standardisiert ist.
Im jadice web toolkit Server steht nun jeweils das aktuelle Token zur jeweiligen Client-ID zur Verfügung.
Vorteile
- Funktionalität unabhängig vom genutzten Protokoll (siehe JWT-3197)
- Einfache Einbindung der Funktionalität → siehe Showcase
- Erkennen und Behandeln von Failover-Fällen: Die Lösung bietet ebenfalls eine Absicherung in Failover-Fällen und sendet in solchen das Token erneut an den Server.
Die API im Detail
Clientseitig ist das aktuelle Token über die ServerConnection
zu setzen - und zwar immer dann, sobald ein neues Token vorliegt:
ServerConnection.get().setAuthenticationInfo(token);
Serverseitig wird der DocumentDataProvider
über eine Factory erzeugt, die das jeweils aktuelle Token aus dem AuthenticationManager
ausliest und dem DocumentDataProvider
übergibt. Dieser kann mit Hilfe des Tokens selbst eine Validierung vornehmen oder das Token zur Authentisierung an den Backend-Service weiterleiten.
public class DataProviderFactoryWithAuthentication implements ContextualFactory<DataProviderWithAuthentication> { @Override public DataProviderWithAuthentication create(InvocationContext context) { String authenticationInfo = AuthenticationInfoManager.getInstance().getAuthenticationInfo(context.getClientId()); return new DataProviderWithAuthentication(authenticationInfo); } }
Gleiches gilt für ServerOperations
, die eine Authentisierung erfordern (z.B. zum Speichern von Annotationen). Auch hier kann über eine ContextualFactory
das jeweils aktuelle Token ausgelesen und in die ServerOperation
übergeben werden.
Variante: Session-Cookies kombiniert mit JSON Web Tokens
Neben der direkten Übergabe von Tokens werden auch kombinierte Varianten vom jadice web toolkit unterstützt - beispielsweise im Zusammenspiel mit WebSeal als Security Proxy. Dies setzt allerdings eine HTTP-basierte Kommunikation voraus, funktioniert also nur mit Longpoll oder Server Sent Events als Transportprotokoll im jadice web toolkit!
Das Szenario sieht hierbei wie folgt aus:
- Requests der Business-Anwendung werden über WebSeal geleitet
- WebSeal stellt ein JSON Web Token aus und reicht es nach hinten an den Server mit der Business Application weiter
- Der Business-Client bekommt nun ein Session-Cookie ausgestellt. (Der Business-Client ist hierbei ebenfalls eine Web-Applikation, z.B. ein Fenster mit IFrame, in dem das jadice web toolkit gestartet wird.)
- Die URL zum Aufruf des jadice web toolkit wird so umgebogen, dass WebSeal angesprochen wird.
- WebSeal identifiziert den Call über das Session-Cookie, das auch hier vom Browser mitgeschickt wird.
- WebSeal reicht das zugehörige JSON Web Token im Html Header mit nach hinten zum jadice web toolkit Server
- Der jadice web toolkit server reicht nun das Token an den Archiv-Service weiter, der dieses wiederum zur Authentisierung + Autorisierung nutzt
Im jadice web toolkit wird dies folgendermaßen umgesetzt:
- Einen javax.servlet.Filter implementieren, der das JWT der HttpSession als Attribut setzt.
- Sowohl den DocumentDataProvider als auch die ServerOperation über eine com.levigo.jadice.web.server.ContextualFactory erzeugen, damit dort aus der HttpSession das JSON Web Token extrahiert und an den Archiv-Service weitergereicht werden kann.
zu 1. Der Filter holt aus dem HttpServletRequest das JSON Web Token und setzt es als Attribut der HttpSession. Das Token wird hierbei durch WebSeal im Http-Header übergeben:
@Override public void doFilter(...) { HttpServletRequest r = (HttpServletRequest) request; String authToken = r.getHeader("Authorization"); r.getSession().setAttribute(JWT_KEY, authToken); }
zu 2. Beim Erzeugen von DocumentDataProvider
bzw. ServerOperation
holen wir über eine ContextualFactory die HttpSession und füttern die Instanzen damit. Beispiel:
import javax.servlet.http.HttpSession; import com.levigo.jadice.web.server.ContextualFactory; import com.levigo.jadice.web.server.DocumentDataProvider; import com.levigo.jadice.web.server.InvocationContext; import com.levigo.jadice.web.server.ServletInvocationContext; @Component public class MyDataProviderFactory implements ContextualFactory<MyDataProviderSource, MyDataProviderHandle> { @Override public MyDataProvider<MyDataProviderSource, MyDataProviderHandle> create(InvocationContext context) { ServletInvocationContext servletInvocationContext = (ServletInvocationContext) context; HttpSession session = servletInvocationContext.getSession(); return new MyDataProvider(session); } }
Ein grundsätzliches Beispiel zur Implementierung findet sich im Showcase zur HttpSessionServerOperationFactory. Die Registrierung der ServerOperation-Factory ist im Referenzhandbuch beschrieben.