Webservices und Client Server Konzepte: Unterschied zwischen den Versionen
Drlue (Diskussion | Beiträge) |
Drlue (Diskussion | Beiträge) |
||
| Zeile 165: | Zeile 165: | ||
'''Base64 Encodiert (Luke:Luki)''': THVrZTpMdWtp<br> | '''Base64 Encodiert (Luke:Luki)''': THVrZTpMdWtp<br> | ||
Der Request (als Beispiel ein GET Request) würde dann folgendermaßen aussehen: | Der Request (als Beispiel ein GET Request) würde dann folgendermaßen aussehen: | ||
<syntaxhighlight lang='http'> | <syntaxhighlight lang='http' line> | ||
GET /customer/101 HTTP/1.1 | GET /customer/101 HTTP/1.1 | ||
Host: webservice.drlue.at | Host: webservice.drlue.at | ||
Version vom 12. Januar 2021, 18:04 Uhr
In den folgenden Abschnitten soll geklärt werden, was ein Webservice ist und worum es sich im spezillen bei RESTful Webservices handelt.
Webservices
Zu allererst soll geklärt werden, was ein Webservices ist:
Ein Webservice (auch Webdienst) stellt eine Schnittstelle für die Maschine-zu-Maschine- oder Anwendungs-Kommunikation über Rechnernetze wie das Internet zur Verfügung. Dabei werden Daten ausgetauscht und auf entfernten Computern (Servern) Funktionen aufgerufen. Jeder Webservice besitzt einen Uniform Resource Identifier (URI), über den er eindeutig identifizierbar ist, sowie je nach Implementierung eine Schnittstellenbeschreibung in maschinenlesbarem Format (als XML-Artefakt, z. B. WSDL), die definiert, wie mit dem Webservice zu interagieren ist. Die Kommunikation kann über Protokolle aus dem Internetkontext wie HTTP oder HTTPS erfolgen; über diese Protokolle wiederum kann beispielsweise XML oder JSON übertragen werden. Ein Webservice ist plattformunabhängig und steht in der Regel mehreren Programmen zum Aufrufen bereit. [1]
Was bedeutet dies nun ganz stark vereinfacht. Ein Webservice ist ein Programm, welches auf einem Server läuft, der über das Internet erreichbar ist. Ein Client (Browser, Anwendung, App), sendet Anfragen an diesen Webservice mit einem standardisierten Protokoll (z.B.: HTTP) und erhält darauf Antworten.
Im folgenden wollen wir uns nun mit RESTful Webservices beschäftigen, diese erfreuen sich großer beliebtheit und stehen in Konkurrenz zu anderen Varianten wie z.b.:
- WSDL/Soap basierten Webservices - Stark antiquiertes Konezpt
- GraphQL basierten Webservices - Damit werden unzulänglichkeiten bei RESTful Webservices behoben, jedoch wird auch auf die einheitliche Schnittstelle verzichtet
RESTful Webservice
Ein RESTful Webservice ist nicht eine konkrete Implementierung, es ist vielmehr ein Architekturstil dem gefolgt werden soll. Dieser besteht aus Prinzipien und Eigenschaften die auf einen Webservice zutreffen müssen, damit es sich um einen RESTful Webservice handelt bzw. einen Webservice der auf den REST Prinzipien beruht. REST steht für Represential State Transfer und wurde von Roy Fielding entworfen.
Prinzipien
Im folgenden wollen wir auf einige der Prinzipien eingehen. Folgender Abschnitt ist jedoch nicht vollständig, soll aber an dieser Stelle ausreichen.
Einheitliche Schnittstelle
Dies ist ein Hauptunterscheidungsmerkmal zu den meisten anderen Webservice Architekturen. Die einheitliche Schnittstelle besteht aus vier weiteren Eigenschaften, wobei wir im folgenden nur drei besprechen werden. Das Ziel ist, wie der Name schon sagt, eine einheitliche Schnittstelle für Webservices und eine daraus folgende einfache Nutzbarkeit.
Jede Ressource hat einen eindeutigen Bezeichner, URI Uniform Resource Identifier, und ist über eine URL erreichbar ist. Eine Ressource kann z.B. ein Kundenprofil sein.
Kunde mit der id 101 -> https://webservice.drlue.at/customer/101
Die Manipulation soll einheitlich erfolgen, dafür bieten sich die HTTP Methoden an. Betrachten wir das Beispiel des Kunden mit der Id 101 und stellen den Vergleich zu den HTTP Methoden her:
| HTTP Methode | URL | Operation |
|---|---|---|
| GET | https://webservice.drlue.at/customer/101 | Holen der Benutzerinformation |
| DELETE | https://webservice.drlue.at/customer/101 | Löschen des Benutzers |
| PUT | https://webservice.drlue.at/customer/101 | Verändern des Benutzers, es müssen die zu ändernden Daten mitgesendet werden |
| POST | https://webservice.drlue.at/customer | Anlegen eines neuen Benutzers, die Benutzerdaten müssen mitgesendet werden |
Weiters soll die Repräsentation der Ressourcen unabhängig von deren eigentlicher Repräsenation z.B.: auf der Datenbank sein. Der Webservice soll es ermöglichen, die Repräsenation auszuwählen. Eine Webseite fordert die Daten beispielsweise als HTML, ein Android Client als JSON. Das Angeforderte Format wird über den HTTP Accept Header mittels eines MIME-Types festgelegt.
Der MIME-Type (Multipurpose Internet Mail Extensions type) oder Mediatype besteht aus folgendem Format: type/subtype.
Weiters können mehrere mögliche Mediatypes angegeben und mit einer Gewichtung versehen werden (was wäre mir das liebste Format).
Anbei ein Beispiel wie dies in Node.js mit dem Express Framework realisiert werden kann.
app.get('/user/:id', async (req, res) => {
var user = User.findById(req.params.id);
if(req.accepts('html')) {
//HTML rendern wenn html akzeptiert wird
res.render('userprofile', { user: user });
} else {
//Wenn html nicht akzeptiert wird, wird json zurückgegeben
res.json(user);
}
}
| Accept Header | Bedeutung |
|---|---|
| application/json | JSON wird zurückgegeben |
| text/html | HTML wird zurückgegeben |
| image/png | Bild wird als PNG angefordert |
| image/jpeg | Bild wird als JPEG angefordert |
Hypermedia as the Engine of Application State (HATEOAS)
Laut Fielding die wichtigste Eigenschaft. Diese Eigenschaft wird jedoch weit nicht in allen Webservices welche sich als RESTful bezeichnen implementiert. Anbei ein sehr einfaches Beispiel welches das Prinzip beschreiben soll. Eine Kundenressource wird mittels GET Request angefordert:
{
id: "123",
name: "Luke",
link: {
messages: "/user/123/messages",
ban: "/user/123/ban"
}
}
Die Response liefert sämtliche Operationen oder weiterführende Ressource, die bei dieser Ressource zur Verfügung stehen.
Caching
HTTP Caching soll implementiert werden. Wobei jede Anfrage die nicht gestellt werden muss, die beste ist.
Klären wir zuerst den Begriff des Cachings. Dieses Konzept gilt natürlich in vielen Bereichen der Informatik, wir erklären den Begriff anhand des Zugriffs auf eine Ressource. Eine Ressource wird geholt, zu einem späteren Zeitpunkt wird diese Ressource erneut geholt. Hat sich diese nicht verändert, so soll sie nicht erneut übertragen werden, sondern aus dem Zwischenspeicher der Anwendung abgerufen werden.
Durch Caching bei Webanwendungen soll die Übertragung von Daten über das Internet reduziert werden.
In folgender Tabelle wird die Funktionsweise von HTTP Caching mittels ETag beschrieben. Man beachte, die HTTP Anfragen sind nicht vollständig und beschränken sich auf die fürs Caching wesentlichen Header.
| Anfrage | Antwort | Beschreibung |
|---|---|---|
GET /customer/101 HTTP/1.1
Host: webservice.drlue.at
|
HTTP/1.1 200 OK
Etag: "478fb2348f700"
{
"name": "luke",
"id": 101
}
|
Erstmaliges holen der spezifischen Ressource. Server Antwortet mit den Daten und liefert im HTTP Header den ETag mit |
GET /customer/101 HTTP/1.1
Host: webservice.drlue.at
If-none-match: "478fb2348f700"
|
HTTP/1.1 304 Not Modified
Etag: "478fb2348f700"
|
Eine weitere Anfrage des Clients erfolgt mit dem ETag nun aber im If-none-match Header. Man könnte diese Anfrage wie folgt interpretieren: Gib mir die Ressource nur zurück, wenn der ETag nicht mehr zutrifft. Der Server Antwortet mit dem HTTP Statuscode 304 Not Modified. Dies bedeutet, es hat sich nichts geändert. Der Client wird somit darauf hingewiesen die Daten aus seinem eigenen Cache oder Zwischenspeicher zu holen. |
GET /customer/101 HTTP/1.1
Host: webservice.drlue.at
If-none-match: "478fb2348f700"
|
HTTP/1.1 200 OK
Etag: "497fb2348f705"
{
"name": "luke1",
"id": 101
}
|
Zu einem späteren Zeitpunkt holt der Client erneut die Selbe Ressource und sendet wieder den ETag mit. Die Ressource hat sich jedoch am Server geändert. Dieser sendet die Ressource dem Client zurück und sendet zusätzlich im Header den neuen ETag mit. |
Mögliche Implementierung
In modernen Technologien wie z.B.: Express für Node.js finden sich automatische mechanismen für die Caching implementierung über ETags. Der Ablauf ist denkbar einfach. Der Server betrachtet vor dem Absenden der Antwort an den Client deren Inhalt. Es geht hier nur um den HTTP Body, die Header spielen hier keine Rolle. Aus dem Body wird eine Hashsumme berechnet und diese wird dann als ETag verwendet.
Hashsumme: Mittels eines Hashalgorithmus wie z.b.: (SHA256, MD5, ...) kann aus einer großen Datenmenge ein "eindeutiger" Wert berechnet werden. Bei SHA256 hat dieser Wert immer eine Länge von 256 Bit, egal wie groß der Input ist. Der Wert kann natürlich nicht eindeut sein, doch die Chance ist verschwindend gering, dass für unterschiedliche Input Daten, die selbe Hashsumme gebildet wird.
Diese Implementierung bietet eine einfache Möglichkeit des Cachings um die Datenübertragung vom Client zum Server deutlich zu reduzieren. Dies reduziert die Netzwerklast auf Client und Serverseite.
Eine Rechenauslastung auf Seiten des Servers findet jedoch nicht statt. Der Body der Antwort muss bei jeder Antwort erstellt werden um den ETag zu generieren. Das bedeutet, der Server muss trotzdem Anfragen an die Datenbank stellen bzw. alle nötigen Berechnungen ausführen um die Antwort zu erstellen.
Weiters ist das Bilden der Hashsumme, vorallem wenn es sich um größere Datenmengen handelt, eine Rechenintensive Operation. Ändern sich die Daten regelmäßig, so könnte man sich das bilden der Hashsumme und den daraus resultierenden mehraufwand, sparen.
Zustandslosigkeit
Jede Nachricht zum oder vom Webservice ist Zustandslos. Dies bedeutet, die Nachricht steht alleine für sich und beinhaltet alle nötigen Informationen damit diese bearbeitet werden kann. Das bedeutet, es besteht keine Verknüpfung zwischen den Nachrichten. Das bedeutet natürlich nicht dass es bei Requests keinen Zusammenhang geben kann. Wie z.B.: Bevor ein Benutzer eine Nachricht senden kann, benötigt er natürlich ein Profil. Somit muss der Webservice die Verbindung eines Clients und dessen Zustand auf dem Server speichern. Dies begünstigt bei Servern die horizontale Skalierbarkeit.
Es gibt zwei Arten der Skalierung: * Vertikale Skalierung - Mehr Rechenpower auf einem Server * Horizontale Skalierung - Verteilung der Rechenlast über mehrere Server
Da die Nachrichten zustandslos sind, ist egal an welchen Server diese übermittelt werden. Gibt es mehrere Anwendungsserver so müssen diese natürlich die Daten untereinader synchron halten. Dies kann z.B.: über einen einzelnen Datenbank Server (oder eine Datenbank Cluster) realisiert werden.
Probleme bei der Zustandslosigkeit
Ein Problem bei der Anforderung der Zustandslosigkeit, stellt die Authentifizierung dar. Somit müsste der Client bei jeder Anfrage an den Webservice die kompletten Anmeldeinformationen mitsenden.
Mögliche komplett zustandslose Lösungen
Eine mögliche Variante für eine komplett zustandslose Authentifizierung bietet Basic Auth. Im HTTP Header Authorization wird der Benutzername und das Passwort mitgesendet. Der Benutzername und das Passwort werden mit einem : getrennt und Base64 ([Encodieren], [Decodieren]) codiert.
Benutzername: Luke
Passwort: Luki
Base64 Encodiert (Luke:Luki): THVrZTpMdWtp
Der Request (als Beispiel ein GET Request) würde dann folgendermaßen aussehen:
GET /customer/101 HTTP/1.1
Host: webservice.drlue.at
Authorization: Basic THVrZTpMdWtp
Hierbei handelt es sich nun um eine zustandslose Authentifizierung, da die komplette Anmeldeinformation jedesmal mitgesendet wird. Dadurch muss der Server natürlich auch bei jedem Request eine Überprüfung der Zugangsdaten vornehmen.
Lösung über Session und Cookie
Nach der Authentifizierung sendet der Server mittels des Response Headers Set-Cookie ein Cookie mit und erstellt somit eine Session - Sitzung. Das Cookie wird auf dem Server verschlüsselt und kann nicht durch den Client entschlüsselt und die Daten im Klartext gelesen werden. Es enthält einen Session Identifier welcher eine Zuordnung zur Session am Server dient. Die Session am Server kann alle möglichen Informationen enthalten, die der Identifikation des Benutzers dienen (z.B.: ID). Der Client (z.B.: Browser) verwendet nun dieses Cookie bei jedem Request und sendet es im Header Cookie mit. Um die Zuordnung zu gewährleisten muss der Server die Session selbst speichern, dies kann aber beispielsweise auch über eine Datenbankanbindung realisiert werden, somit haben mehrere Anwendungsserver die Möglichkeit eine Session zuzuordnen und zu verifizieren.
Weiters hat eine Session eine gewisse Gültigkeitsdauer. Wird die Session über eine längere Zeit nicht verwendet, so verliert diese ihre Gültigkeit.
Somit handelt es sich hier um eine stateful Authentication da Daten auf dem Server gespeichert werden müssen.
Token basierte Authentifizierung
Eine komplett Zustandslose Authentifizierung, ist die Authentifizierung mittels Token. Ein Beispiel wäre hierfür der JWT Token. Der Mechanismus funktioniert ähnlich wie bei der Session, jedoch enthält der Token die Informationen selbst und nicht nur eine Zuordnung zu einer Session.
Meist besteht ein Authentifizierungstoken aus zwei Bestandteilen:
- Auth Token - Der eigentliche Token, welcher für die ständige Authentifizierung verwendet wird. Dieser hat meist nur eine sehr kurze Gültigkeit (z.B.: 30 Minuten).
- Refresh Token - Dieser Token hat meist eine sehr lange Gültigkeit und wird verwendet um den Auth Token zu aktualisieren. Bei der Aktualisierung kann beispielsweise geprüft werden, ob eine erneute Authentifizierung erfolgen darf.
Bei einer Token basierten Authentifizierung können wir von einer stateless Authentication sprechen.
Mehrschichtige Systeme
Die Webservice Systeme sollen mehrschichtig aufgebaut werden, der Zugriff auf das System soll aber nur über eine Schnittstelle erfolgen. Der Aufbau kann wie folgt aussehen.
- Loadbalancer - z.B.: Nginx
- Mehrere Anwendungsserver - z.B.: Node.js Express Server
- Datenbankserver - z.B.: Mongodb
Clients stellen die Anfragen nur an den Loadbalancer. Dieser verteilt die Anfragen gleichmäßig über alle Anwendungsserver welche die Anfrage bearbeiten und beantworten. Diese kommunizieren wenn benötigt mit dem Datenbankserver. Der Client selbst kommuniziert niemals mit dem Datenbankserver.
Quellen
https://de.wikipedia.org/wiki/Representational_State_Transfer
https://www.ionos.at/digitalguide/websites/web-entwicklung/hateoas-alle-informationen-zu-der-rest-eigenschaft/
https://medium.com/@kennch/stateful-and-stateless-authentication-10aa3e3d4986