1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
\section{Entwurfsmuster und Techniken}
\subsection{Entwurfsmuster}
\subsubsection{Dependency Injection}
Die Dependency Injection (dt. Abhängigkeitsinjektion) ist ein Entwurfsmuster, welches die Abhängigkeiten von Objekten bestimmt und an einem zentralen Ort speichert sowie verwaltet.
Sollte ein Objekt also von einem anderen Objekt abhängig sein, so wird an diesem zentralen Ort nach der Abhängigkeit gesucht.
Ist die Abhängigkeit vorhanden, so wird dieses Objekt dann an dem benötigten Ort eingesetzt (injiziert).
Dies geschieht während der Laufzeit.
Der zentrale Ort, an dem die Abhängigkeiten gespeichert werden, wird meist von einem Framework verwaltet.
Im Falle dieses Projekts ist \Gls{spring} das Framework und der \Gls{spring} Container der zentrale Ort, an dem die Abhängigkeiten gespeichert werden.
Der Vorteil dieses Entwurfsmusters ist, dass Objekte von anderen Objekten abgekoppelt werden, sprich: Das Objekt mit der Abhängigkeit muss nicht mehr von der expliziten Klasse Kenntnis haben und es kann nur mit Interfaces gearbeitet werden, was in den \Gls{solid}-Kriterien das D für Dependency Inversion erfüllt.
Ein weiterer Vorteil ist, dass die Abhängigkeiten innerhalb einer Konfigurationsdatei definiert werden können.
Sprich: Man kann mehrere Implementierungen besitzen, die alle das gleiche Interface implementieren und kann in der Konfigurationsdatei angeben, welche Implementierung gewählt werden soll.
\subsubsection{Data Access Object (DAO)}
\label{DAO_Pattern}
Das Data Access Object (kurz: DAO, dt: Datenzugriffsobjekt) ist ein Entwurfsmuster, das eingesetzt wird um den Zugriff auf \Gls{db}en zu vereinfachen und die \Gls{business} von der Datenzugriffslogik zu trennen.
Dazu gibt es zwei Komponenten: das DAO-Interface und die DAO-Implementierung.
Das DAO-Interface wird von allen DAO-Implementierungen implementiert und bietet alle Datenzugriffsfunktionen an, auf die die \Gls{business} zugreift.
Die DAO-Implementierung ist eine Klasse, die das DAO-Interface implementiert und den tatsächlichen Zugriff auf die \Gls{db} ausführt.
Der Vorteil dieses Entwurfsmusters ist es mehrere Implementierungen desselben DAO-Interfaces zu besitzen.
In Kombination mit der Dependency Injection ist es einfach zwischen den Implementierungen für verschiedene \Gls{db}en (bspw. MariaDB und My\Gls{SQL}) zu wechseln.
Damit wird der Datenzugriff flexibler.
Im Falle dieses Projekts wird eine DAO-Implementierung für MariaDB verwendet.
Ein weiterer Vorteil ist die zuvor angesprochene Trennung der Geschäfts- und Datenzugriffslogik.
Da sich die \Gls{business} und Datenzugriffslogik mithilfe dieses Musters in verschiedenen Komponenten befinden, sind diese voneinander getrennt und es wird einfacher die jeweiligen Implementierungen zu testen.
Gleichzeitig verbessert sich damit die Wiederverwendbarkeit des Codes, da die DAO-Implementierungen in anderen Programmen, die mit demselben DAO-Interface arbeiten, eingesetzt werden können.
Damit erfüllt das DAO-Muster die Kriterien S und O der \Gls{solid}-Kriterien.
Das Single-Responsibility Prinzip wird erfüllt, da der Zugriff auf die \Gls{db} von der \Gls{business} getrennt wird und damit die DAO-Implementierung alleine für den Zugriff auf die \Gls{db} verantwortlich ist.
Das Open/Closed Prinzip wird erfüllt, da die DAO-Implementierung erweitert werden kann, ohne dass der Rest vom Projekt betroffen wird und außerhalb der Klasse nur mit dem DAO-Interface gearbeitet werden kann.
\subsection{Techniken}
\subsubsection{JSON Web Token}
\Gls{json} Web Token (JWT) ist ein offener Standard der in RFC 7519 definiert wird.
Mit einem JWT ist es möglich Informationen sicher in einem kodierten \Gls{json} Objekt zu übertragen.
Die Sicherheit der Daten wird dabei durch eine digitale Signatur gewährleistet.
Ein JWT besteht aus drei durch Punkte ('.') voneinander getrennten Teilen:
\texttt{Header}, \texttt{Payload} und \texttt{Signatur}.
Der \texttt{Header} besteht dabei typischerweise aus der Information um welchen Typ von Token es sich handelt, also einen JWT,
und der Information welcher Signierungs-Algorithmus verwendet wird.
Diese Informationen werden \Gls{Base64} kodiert und bilden den ersten Teil des JWT.
Im \texttt{Payload} Teil werden die eigentlichen Informationen \Gls{Base64} kodiert.
Die \texttt{Signatur} ergibt sich durch die mit einem Punkt voneinander getrennten Kodierungen des \texttt{Headers} und
des \texttt{Payload}-Teils. Diese Zeichenkette wird dann mit einem geheimen Schlüssel
durch den im \texttt{Header} angegebenen Signierungs-Algorithmus signiert.
JWT werden einmalig vom Server erzeugt und beim Client gehalten. Daher ist es
nicht notwendig wie z.B. bei \gls{cookie} basierten Sessions, eine Liste mit gültigen Sessions auf dem Server zu verwalten, was bei mehreren Servern schwierig ist.
In diesem Projekt werden JWT zur Verifikation der E-Mail-Adresse eines Benutzers und zur Überprüfung der Autorisation bei Anfragen an den Server verwendet.
Zur Bestätigung der E-Mail-Adresse speichert der Server die zur Verifikation des
Benutzers benötigten Daten in einem JWT. Dieser wird in der URL des Verifikations-Links kodiert. Wenn der Benutzer den Verifikations-Link anklickt,
wird der JWT an den Server weitergeleitet. Dieser überprüft die Signatur des JWT ihn mit seinem geheimen Schlüssel
und kann so die Verifikation der E-Mail-Adresse abschließen.
Bei dem Login-Vorgang sendet der Client zuerst seine Anmeldedaten (Benutername und Passwort), um sich zu authentifizieren.
Der Server überprüft die Angaben, generiert einen JWT und gibt diesen zurück, falls die Daten korrekt sind.
Bei späteren Anfragen an den Server übermittelt der Client diesen JWT. Der Server überprüft die Validität des JWT und trifft
basierend darauf die Entscheidung, ob die Anfrage bearbeitet oder abgelehnt wird.
\subsubsection{Objektrelationale Abbildung (Object-relational mapping)}\label{t:orm}
Objektrelationale Abbildung - kurz ORM von der englischen Bezeichnung \enquote{Object-relational mapping} - ist eine Technik der Softwareentwicklung.
Sie widmet sich dem, mit der persistenten Speicherung von Laufzeit-Objekten zusammenhängenden \enquote{Impedance mismatch} Problem.
Dieses beschreibt die Diskrepanz zwischen den in der Pogrammier- beziehungsweise \Gls{db}welt vorherrschenden Konzepten - nämlich der objektorientierten Programmierung und relationalen \Gls{db}en.
So speichern objektorientierte Programmiersprachen Daten und Verhalten in Objekten mit eindeutiger Identität, wobei Zustand und Verhalten hinter einer Schnittstelle verborgen werden.
Relationale \Gls{db}en hingegen speichern Daten in Tabellen und basieren auf dem mathematischen Konzept der Relationenalgebra.
Objektrelationale Abbildung bietet eine Möglichkeit diese Diskrepanz zu vermindern, indem sie ein Mapping zwischen Objekten und Datenstrukturen relationaler \Gls{db}en herstellt.
Einem, in einer objektorientierten Programmiersprache geschriebenen, Anwendungsprogramm erscheint dann die verbundene relationale \Gls{db} als objektorientierte \Gls{db}.
Durch ORM wird also sowohl das Ablegen von Objekten mit Attributen und Methoden in relationale \Gls{db}en, als auch das Erzeugen von solchen Objekten aus entsprechenden Datensätzen ermöglicht.
Vorteilhaft ist daran außerdem, dass die objektorientierte Programmiersprache nicht erweitert werden muss.
Des Weiteren existiert für jede Umgebung ausgereifte Software für die Verwendung relationaler \Gls{db}en.
Allerdings ist der Schritt in Richtung objektorientiertem Ansatz immanent ein Schritt weg von den eigentlichen Stärken relationaler \Gls{db}en.
Der grundlegende Ansatz ist die Abbildung von Klassen auf Tabellen, wobei die Spalten den Attributen und die Zeilen den Objekten der Klasse zugeordnet sind.
Dabei entspricht der Primärschlüssel der Tabelle der Objektidentität und Objektreferenzen werden mithilfe von zusätzlichen Fremdschlüsseln repräsentiert.
%Um Vererbung abzubilden gibt es drei grundlegende Möglichkeiten.
%Erst einmal kann für eine Vererbungshierarchie auch genau eine gemeinsame Tabelle mit allen Attributen verwendet werden, in der ein Diskriminator bestimmt, zu welcher Klasse ein Objekt gehört.
%Als zweite Option kann pro Unterklasse eine zusätzliche verknüpfte Tabelle eingeführt werden.
%Letztlich kann auch pro konkreter Klasse eine Tabelle verwendet werden, wobei die Tabelle für die abstrakte Basisklasse entfällt.
Die von diesem Mapping betroffenen Klassen aus dem Model-Paket (\ref{p:model}) des Backends sind User, SubscriptionAction, Subscription, EpisodeAction und \Gls{episode}.
Konkret für dieses Projekt findet ORM als Technik durch die Implementierung der Jakarta Persistence \Gls{api} (JPA) Anwendung.
Dafür wird das von \Gls{spring} zur Implementierung von JPA-basierten Datenzugriffsschichten bereitgestellte Modul \Gls{spring} Data JPA genutzt.
Als JPA-Implementation wiederum wird das Open-Source-Persistenz- und ORM-Framework Hibernate für \Gls{java} verwendet.
Dabei erfolgen Abfragen der persistierten Objekte über die Abfragesprache Jakarta Persistence Query Language (JPQL), welche dann mittels JDBC in den entsprechen \Gls{SQL}-Dialekt für MariaDB übersetzt.
%Hier sei angemerkt, dass JPQL eine Untermenge der Hibernate Query Language (HQL) ist.
\newpage
|