distributed-rest.html 49.9 KB
Newer Older
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
<p>REST-Webservices basieren wie SOAP-Webservices auf dem HTTP-Protokoll, über das sämtliche Nachrichten zwischen Client und Server ausgetauscht werden. Damit ist ein REST-Webservice auch grundlegend auf synchrone Kommunikation ausgelegt. Die Nachrichten werden üblicherweise ebenso wie bei SOAP in einem standardisierten, textbasierten und damit technologieunabhängigen Dateiformat wie <a href="https://www.w3.org/XML/">XML</a> oder <a href="https://www.json.org/">JSON</a> ausgetauscht. Demzufolge sind auch REST-Webservices unabhängig von einer konkreten Laufzeitumgebung oder Programmiersprache, so dass Client und Server in ganz unterschiedlichen Sprachen implementiert werden können und als Komponenten klar voneinander abgegrenzt sind. Dies führt wie gewünscht zu einer losen Kopplung der Komponenten in der Systemarchitektur.</p>

<h4>Prinzipien von REST</h4>

<p>Der Begriff REST steht für <i>Representational State Transfer</i> und geht auf die <a href="https://www.ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation.pdf">Dissertation von Roy Fielding</a> im Jahr 2000 zurück. Die wesentliche Idee besteht darin, alle Funktionalität über eindeutig adressierbare Ressourcen bereitzustellen – und nicht wie bei RPC-Ansätzen (SOAP, RMI) über Methoden.
Der prinzipielle Ablauf der Kommunikation zwischen Client und Server ist in der folgenden Abbildung skizziert.</p>

<img src="media/distributed_rest_client_server.png" style="width:600px">
<label>Ablauf der Kommunikation bei REST-Webservices</label>

<span>Zur Adressierung einer Ressource dient eine <a href="https://de.wikipedia.org/wiki/Uniform_Resource_Identifier"><i>URI (Unique Resource Identifier)</i></a>, die allgemein nach dem Muster <code>http://&lt;host>:&lt;port>/&lt;context-path>/&lt;resource-path></code> aufgebaut ist.</span>
<ul>
<li>Die URI für die Ressource eines einzelnen Bankkontos mit der Id 1 könnte sein: <code>http://example.com/banking/accounts/1</code></li>
<li>Auch Mengen sind Ressourcen. So ergibt sich als URI für die Menge aller Bankkonten entsprechend: <code>http://example.com/banking/accounts</code></li>
</ul>

<p>REST ist kein Protokoll sondern ein Architekturstil, der auf Konventionen basiert. Es gibt keine Schnittstellenbeschreibungssprache (<a href="https://en.wikipedia.org/wiki/Interface_description_language"><i>Interface Definition Language</i></a>, kurz <i>IDL</i>) wie z.B. WSDL bei SOAP-Webservices. Eine wichtige Konvention ist die Einhaltung eines <i>Uniform Interface</i>, demzufolge die HTTP-Methoden je Ressource wie folgt abzubilden sind:</p>

<ul>
<li><b>GET</b>: Lesen des aktuellen Zustands einer Ressource.</li>
<li><b>POST</b>: Erzeugen einer neuen Ressource.</li>
<li><b>PUT</b>: Ersetzen einer bestehenden Ressource als Ganzes.</li>
<li><b>PATCH</b>: Aktualisieren einer bestehenden Ressource in Teilen, d.h. Ändern ausgewählter Attribute.</li>
<li><b>DELETE</b>: Löschen einer bestehenden Ressource.</li>
</ul>

<p>Es ergeben sich folgende typische Operationen auf einer Ressource, die bei einer Anfrage an der URI entsprechend der gewählten HTTP-Methode ausgeführt werden. Bezüglich der leeren Zellen der Tabelle werden i.d.R. keine Operationen von einem REST-Webservice unterstützt, da z.B. das Löschen einer Menge von Ressourcen oder deren vollständiges Ersetzen durch eine andere Menge nicht benötigt werden.</p>

<style>
  .rest-resources-table td { width: 15%; }
</style>
<table class="rest-resources-table table table-bordered table-sm">
<tr class="table-head-row"><th>Ressource</th><th>GET</th><th>POST</th><th>PUT</th><th>PATCH</th><th>DELETE</th></tr>
<tr>
	<td>Menge, z.B. <code>/accounts</code></td>
	<td style="width: 25%;">Ausgabe einer Liste aller Elemente der Menge. Es werden i.d.R. nicht alle Attribute der Elemente zurückgegeben, aber mind. deren URIs. Die Liste ist ggf. mittels weiterer Parameter des HTTP-Request sortiert, gefiltert und/oder in paginierte Abschnitte unterteilt.</td>
	<td>Erzeugen eines neuen Elements in der Menge, dessen URI im Header oder Body der HTTP-Response zurückgegeben wird.</td>
	<td></td>
	<td></td>
	<td></td>
</tr>
<tr>
	<td>Element, z.B. <code>/accounts/1</code></td>
	<td>Lesen aller Attribute des Elements.</td>
	<td></td>
	<td>Ersetzen des Elements an der angegeben URI. Erzeugen des Elements, falls es nicht existiert.</td>
	<td>Aktualisieren von ausgewählten Attributen des Elements.</td>
	<td>Löschen des Elements.</td>
</tr>
</table>

<p>Die Ressourcen können vom Server in unterschiedlichen Repräsentationen an einen Client ausgeliefert werden. Die geläufigsten Repräsentationen sind die beiden strukturierten Datenformate XML und JSON, aber eine Ressource könnte auch in HTML, in einem spezifischen Bildformat (JPG, PNG, ...), o.ä. repräsentiert werden. Ein Client kann über den HTTP-Header <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept">Accept</a> angeben, welches Format er in der HTTP-Response erhalten möchte. Falls möglich, liefert der Server genau dieses Format aus, andernfalls ein möglichst ähnliches. Es folgt eine Auswahl häufig verwendeter Formate und ihrer <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types">MIME-Typen</a> nach <a href="https://tools.ietf.org/html/rfc6838">RFC 6838</a>.</p>

<table class="table table-bordered table-sm" style="width: auto;">
<tr class="rest-resources-head"><th>Format</th><th>MIME-Type</th></tr>
<tr><td>JSON</td><td>application/json</td></tr>
<tr><td>XML</td><td>application/xml</td></tr>
<tr><td>HTML</td><td>text/html</td></tr>
<tr><td>Unstrukturierter Text</td><td>text/plain</td></tr>
<tr><td>Spezifische Bildformate</td><td>image/jpeg, image/png, image/gif, ...</td></tr>
<tr><td>Spezifische Audioformate</td><td>audio/mpeg, audio/wav, audio/webm, ...</td></tr>
<tr><td>Spezifische Videoformate</td><td>video/mp4, video/ogg, video/webm, ...</td></tr>
<tr><td>Unspezifisches Binärformat</td><td>application/octet-stream</td></tr>
</table>

<p>Bei einer GET-Anfrage an die Ressource <code>/accounts/1</code> könnte der Body der HTTP-Response, der die Attribute des Kontos mit der Id 1 darstellt, je nach angeforderter Repräsentation exemplarisch wie folgt aussehen:</p>

<ul class="nav nav-tabs" id="rest-accept-tabs" role="tablist">
  <li class="nav-item"><a href="#rest-accept-tabs-xml" class="nav-link active" data-toggle="tab" role="tab">XML (application/xml)</a></li>
  <li class="nav-item"><a href="#rest-accept-tabs-json" class="nav-link" data-toggle="tab" role="tab">JSON (application/json)</a></li>  
  <li class="nav-item"><a href="#rest-accept-tabs-html" class="nav-link" data-toggle="tab" role="tab">HTML (text/html)</a></li>  
</ul>
<div class="tab-content" id="rest-accept-tabs-content">
  <div class="tab-pane show active" id="rest-accept-tabs-xml" role="tabpanel">
	<pre><code class="language-xml line-numbers"><!--<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<account>
    <id>1</id>
    <owner>Hendricks</owner>
    <entries>
		<subject>Credit 1</subject>
        <value>10</value>
        <date>2021-01-30T15:00:00.000+01:00</date>
    </entries>
    <entries>
		<subject>Credit 2</subject>
        <value>20</value>
        <date>2021-01-31T15:00:00.000+01:00</date>
    </entries>
</account> --></code></pre>  
  </div>
  <div class="tab-pane" id="rest-accept-tabs-json" role="tabpanel">
	<pre><code class="language-json line-numbers">{
	"id": 1,
	"owner": "Hendricks",
	"entries": [
		{
			"subject": "Credit 1",
			"value": 10,
			"date": 1612015200
		},
		{
			"subject": "Credit 2",
			"value": 20,
			"date": 1612101600
		}
	]
}</code></pre>  
  </div>
  <div class="tab-pane" id="rest-accept-tabs-html" role="tabpanel">
	<pre><code class="language-html line-numbers"><!--<html>
<body>
	<div id="id"> <label>Account Id</label> 1 </div>
	<div id="owner"> <label>Owner</label> Hendricks </div>
	<div id="balance"> <label>Balance</label> 30 </div>
	<table>
		<tr> <th>Date</th> <th>Subject</th> <th>Value</th> </tr>
		<tr> <td>2021-01-30</td> <td>Credit 1</td> <td>10</td> </tr>
		<tr> <td>2021-01-31</td> <td>Credit 2</td> <td>20</td> </tr>
	</table>
</body>
</html>--></code></pre>  
  </div>  
</div> 

<p>Bei POST-, PUT- und PATCH-Anfragen wird der Client seinerseits Daten in einem bestimmten Format im Body des HTTP-Request an den Server übermitteln wollen. Dazu kann er über den HTTP-Header <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type">Content-Type</a> den Server informieren, in welchem Format er diese Daten sendet.</p>

Weitere Konventionen eines REST-Webservice sind:
<ul>
<li><b>Cachebarkeit</b>: Der lesende Zugriff mittels GET ist seitenfrei, d.h. der Zustand keiner Ressource wird verändert. Dadurch können Clients und Intermediäre (Proxys) die Antworten auf GET-Anfragen cachen, solange dieses nicht explizit durch einen entsprechenden Eintrag im HTTP-Header (<a href="https://developer.mozilla.org/de/docs/Web/HTTP/Headers/Cache-Control">Cache-Control</a>) unterbunden wird. Das Caching kann die Performance der Anwendung verbessern und eingeschränkte Offline-Funktionalität ermöglichen.</li>
<li><b>Idempotenz:</b> Zugriffe auf eine Ressource per GET, PUT und DELETE sind idempotent, d.h. wenn mehrere Zugriffe hintereinander mit den gleichen Parametern ausgeführt werden, ist der serverseitige Zustand derselbe wie bei einem einzelnen Zugriff. Idempotenz erleichtert die Fehlertoleranz, da ein Client im Zweifel, wenn er unsicher ist, ob eine GET-, PUT- oder DELETE-Anfrage den Server erreicht hat, diese Anfrage einfach wiederholen kann. Für POST- und PATCH-Anfragen wird per Konvention keine Idempotenz gefordert.</li>
<li><b>Zustandslosigkeit</b>: Bei einem REST-Webservice ist die Client-Server-Kommunikation grundsätzlich zustandslos, d.h. der Server speichert keinen Kontext des Client zwischen dessen einzelnen HTTP-Requests. Es gibt also serverseitig keine <i>Sessions</i>. Das schließt allerdings nicht aus, dass sämtliche HTTP-Requests vom Server in einer Logdatei protokolliert werden. Jeder Client muss nun selbst den Zustand seiner Session vorhalten und bei jedem HTTP-Request ggf. ein Token zur Autorisierung an den Server mitsenden, falls dieses für den Zugriff auf eine geschützte Ressource erforderlich ist. Demzufolge gibt es i.d.R. als Teil eines REST-Webservice eine bestimmte Ressource zur Authentifizierung, bei der ein Client initial ein entsprechendes Token erhalten kann. Weiter unten gehen wir zu diesem Zweck genauer auf <a href="https://jwt.io/">JSON Web Tokens (JWT)</a> ein </li>
</ul>


<h4>JAX-RS (Java API for RESTful Web Services)</h4>

<p>Um REST-Webservices in Java zu implementierten, ist zunächst die in <a href="https://jcp.org/en/jsr/detail?id=370">JSR 370</a> spezifizierte API namens JAX-RS maßgeblich. Die Referenzimplementierung für JAX-RS ist das Framework <a href="https://jersey.github.io/">Jersey</a>, das wir für das folgende Anwendungsbeispiel verwenden werden. Alternative Implementierungen für JAX-RS sind <a href="https://resteasy.github.io/">RESTEasy</a>, <a href="http://cxf.apache.org/">Apache CXF</a> und <a href="https://restlet.com/">Restlet</a>. Auch mit dem <a href="https://spring.io/">Spring Framework</a> lässt sich einfach ein REST-Webservice in Java implementieren, der aber nicht unbedingt konform zu JAX-RS sein muss, wenn die Implementierung den Empfehlungen von <a href="https://spring.io/guides/gs/rest-service/">Spring MVC</a> folgt.</p>

<p>Wie die meisten aktuellen Java-Frameworks arbeitet auch JAX-RS intensiv mit Annotationen. Diese Annotationen aus dem Package <code>javax.ws.rs</code> werden anhand der folgenden Code-Beispiele sukzessive erklärt. Der Code bezieht sich auf ein Anwendungsbeispiel, in dem erneut – wie in den vorherigen Kapiteln – Bankkonten über eine API verwaltet werden sollen. Um den REST-Webservice zu realisieren, wird passend zur Datenmodellklasse <code>Account</code> eine zugehörige Ressourcenklasse <code>AccountResource</code> entworfen, die über die oben vorgestellten HTTP-Methoden eine Menge von Konten verwaltet. Diese Klasse verwaltet die Konten in einer Map, deren Schlüssel die Konto-Ids sind (Zeile 12). Diese Map ist noch flüchtig und wird erst im nächsten Kapitel in einer relationalen Datenbank gespeichert (s. Kapitel <a href="#unit-orm" class="navigate">Objekt-Relationales Mapping</a>). Da die Klasse <code>AccountResource</code> insgesamt relativ umfangreich ist, werden zunächst nur die Signaturen ihrer Methoden dargestellt und deren Implementierungen anschließend schrittweise ergänzt und erläutert.</p>

<ul class="nav nav-tabs" id="restserver-tabs" role="tablist">
  <li class="nav-item"><a href="#restserver-tabs-starter" class="nav-link" data-toggle="tab" role="tab">WebServiceStarter</a></li>
  <li class="nav-item"><a href="#restserver-tabs-accountresource" class="nav-link active" data-toggle="tab" role="tab">AccountResource</a></li>
  <li class="nav-item"><a href="#restserver-tabs-account" class="nav-link" data-toggle="tab" role="tab">Account</a></li>
  <li class="nav-item"><a href="#restserver-tabs-accountentry" class="nav-link" data-toggle="tab" role="tab">AccountEntry</a></li>
</ul>
<div class="tab-content" id="restserver-tabs-content">
  <div class="tab-pane" id="restserver-tabs-starter" role="tabpanel">
	<pre><code class="language-java line-numbers">import com.sun.net.httpserver.HttpServer;
// ...

public class WebServiceStarter {

    public static void main(String[] args) throws Exception {
        ResourceConfig rc = new ResourceConfig().packages("resources");
        HttpServer server = JdkHttpServerFactory.createHttpServer(URI.create("http://localhost:8080/"), rc);
        System.out.println("Hit enter to stop HTTP server.");
        System.in.read();
        server.stop(0);
    }
}</code></pre>  
  </div>
  <div class="tab-pane show active" id="restserver-tabs-accountresource" role="tabpanel">
	<pre><code class="language-java line-numbers">package resources;

import javax.ws.rs.core.Response;
import javax.ws.rs.Path;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
// ...

@Path("/accounts")
public class AccountResource {

    final static Map&lt;Integer, Account> accounts = new ConcurrentHashMap&lt;>();

    @GET
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})	
    public Collection&lt;Account> getAccounts() { /* ... */ }

    @POST
    @Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response postAccount(Account account) { /* ... */ }	
	
    @GET @Path("{id}")
    public Response getAccount(@PathParam("id") int id) { /* ... */ }

    @PUT @Path("{id}")
    public Response putAccount(@PathParam("id") int id, Account account) { /* ... */ }

    @PATCH @Path("{id}")
    public Response patchAccount(@PathParam("id") int id, Account patchedAccount) { /* ... */ }

    @DELETE @Path("{id}")
    public Response deleteAccount(@PathParam("id") int id) { /* ... */ }
}</code></pre></div>
  <div class="tab-pane" id="restserver-tabs-account" role="tabpanel">
	<pre><code class="language-java line-numbers">package model;

import javax.xml.bind.annotation.XmlRootElement;
// ...

@XmlRootElement // required for XML binding
public class Account {

    public static AtomicInteger nextId = new AtomicInteger(1);

    public int id;
    public String owner;
    public List&lt;AccountEntry> entries;

    public int balance() {
        if (entries == null) return 0;
        return entries.stream().mapToInt(entry -> entry.value).sum();
    }
}</code></pre></div>
  <div class="tab-pane" id="restserver-tabs-accountentry" role="tabpanel">
	<pre><code class="language-java line-numbers">package model;

public class AccountEntry {

    public String subject;
    public int value;
    public Date date;

    public AccountEntry() {
        this.date = new Date();
    }
}</code></pre> 
  </div> 
</div>

<span>Erläuterungen zu den Annotationen aus dem Package <code>javax.ws.rs</code> in dem obigen Code-Beispiel:</span>
<ul>
<li><b>@Path</b>: In der Klasse <code>WebServiceStarter</code> wird ein HTTP-Server gestartet, dem eine Ressourcen-Konfiguration als Argument übergeben wird (s. <code>WebServiceStarter</code>, Zeilen 7-8). Mittels der Ressourcen-Konfiguration wird Jersey angewiesen alle Klassen im Package <code>resources</code> dahingehend zu analysieren, ob sie mit einer Annotation <code>@Path</code> versehen sind (z.B. <code>AccountResource</code>, Zeile 9). Falls diese Annotation vorhanden ist, handelt es sich um eine REST-Ressourcenklasse, die unter dem entsprechenden Pfad erreichbar gemacht wird.</li>
<li><b>@GET</b>, <b>@POST</b>, etc.: Die einzelnen Methoden einer Ressourcenklasse reagieren auf nur spezifische HTTP-Methoden, wenn sie mit einer entsprechenden Annotation versehen sind, z.B. <code>@GET</code> (Zeile 14) oder <code>@POST</code> (Zeile 18).</li>
<li><b>@PathParam</b>: Der Pfad einer Ressourcenklasse kann durch eine entsprechende Annotation an ihren Methoden dynamisch erweitert werden, z.B. <code>@Path("{id}")</code> (Zeile 23). Die angehängte Id wird von Jersey aus der URI extrahiert und mittels der Annotation <code>@PathParam("id")</code> als Methodenargument injiziert (Zeile 24). Hier wird implizit ein Cast von <code>String</code> auf <code>int</code> durchgeführt, der auch fehlschlagen kann.</li>
<li><b>@Consumes</b> und <b>@Produces</b>: An den Methoden einer Ressourcenklasse kann mittels der Annotationen <code>@Consumes</code> (Zeile 19) und <code>@Produces</code> (Zeile 15) explizit angegeben werden, in welchen Formaten die annotierten Methoden Eingaben verarbeiten bzw. Ausgaben erzeugen können. Diese Annotationen sind insbesondere dann notwendig, wenn je nach Eingabeformat (HTTP-Header <code>Content-Type</code>) oder angefordertem Ausgabeformat (HTTP-Header <code>Accept</code>) eine unterschiedliche Methode der Ressourcenklasse ausgeführt werden soll.</li>
</ul>

<p>Die gezeigten Code-Beispiele zu REST-Webservices finden sich im Verzeichnis <a href="/remote/rest" class="repo-link">/remote/rest</a> des Modul-Repository.</p>

<h4>HTTP-Response</h4>

<p>Ein REST-Webservice sollte einem Client grundsätzlich mit <a href="https://www.iana.org/assignments/http-status-codes/">HTTP-Status-Codes</a> antworten, die dem vereinbarten Standard entsprechen. Die folgende Tabelle zeigt eine Auswahl von gebräuchlichen HTTP-Status-Codes, wobei der Bereich 2xx stets für erfolgreiche HTTP-Requests steht, 3xx für Weiterleitungen, 4xx für Fehler des aufrufenden Client und 5xx für Fehler im Server.</p>

<table class="table table-bordered table-sm" style="width: auto;">
<tr class="rest-resources-head"><th>HTTP-Status-Code</th><th>Beschreibung</th></tr>
<tr><td>200</td><td><i>OK</i>, generischer Code für eine erfolgreiche Anfrage</td></tr>
<tr><td>201</td><td><i>Created</i>, neue Ressource erfolgreich angelegt</td></tr>
<tr><td>204</td><td><i>No Content</i>, Anfrage erfolgreich, aber es wird kein Inhalt zurückgegeben</td></tr>
<tr><td>301</td><td><i>Moved Permanently</i>, Ressource ist dauerhaft unter anderer URI erreichbar</td></tr>
<tr><td>304</td><td><i>Not Modified</i>, unverändert, d.h. Cache muss nicht aktualisiert werden</td></tr>
<tr><td>400</td><td><i>Bad Request</i>, generischer Code für einen Fehler des Client in der Parametrisierung der Anfrage</td></tr>
<tr><td>403</td><td><i>Forbidden</i>, Zugriff auf die Ressource ist verboten</td></tr>
<tr><td>404</td><td><i>Not Found</i>, für die URI konnte keine Ressource gefunden werden</td></tr>
<tr><td>500</td><td><i>Internal Server Error</i>, generischer Code für einen nicht weiter behandelten Fehler im Server</td></tr>
<tr><td>503</td><td><i>Service Unavailable</i>, Service ist vorübergehend nicht zu erreichen</td></tr>
</table>

<p>Die beiden Methoden im folgenden Code-Beispiel geben eine Liste aller Konten bzw. ein einzelnes Konto zurück. Der Rückgabetyp der Methode <code>getAccounts</code> (Pfad <code>/accounts</code>) ist <code>Collection&lt;Account></code> (Zeile 2). Für diese Methode wird die HTTP-Response implizit durch das JAX-RS-Framework zusammengestellt. Die Liste der Konten wird ohne Zutun des Entwicklers in eine JSON- oder XML-Repräsentation transformiert und in ein Objekt vom Typ <code>javax.ws.rs.core.Response</code> eingebettet, dessen Status-Code 200 ist. Bei der zweiten Methode <code>getAccount</code> (Pfad z.B. <code>/accounts/1</code>) wird das <code>Response</code>-Objekt explizit zusammengestellt (Zeilen 10 und 12). Die Klasse <code>Response</code> bietet dazu statische Methoden um den Status-Code (Methode <code>status</code>), den Body (Methode <code>entity</code>) und spezifische Header-Attribute (Methode <code>header</code>) zu setzen.</p>

<pre><code class="language-java line-numbers">@GET
public Collection&lt;Account> getAccounts() {
	return accounts.values();  // return code is 200
}

@GET @Path("{id}")
public Response getAccount(@PathParam("id") int id) {
	Account account = accounts.get(id);
	if (account == null) {
		return Response.status(Response.Status.NOT_FOUND).build(); // return code is 404
	}
	return Response.status(Response.Status.OK).entity(account).build(); // return code is 200
}</code></pre> 

<p>Im Body eines HTTP-Request und einer HTTP-Response wird i.d.R. JSON oder XML von einem REST-Webservice empfangen bzw. von diesem versendet. Das JAX-RS-Framework übernimmt implizit die Transformation von Java-Objekten in JSON/XML und andersherum. Dazu wird jeweils eine entsprechende Bibliothek benötigt. Diese Bibliotheken sind üblicherweise <a href="https://github.com/FasterXML/jackson">Jackson</a> für das JSON-Binding und <a href="https://github.com/eclipse-ee4j/jaxb-ri">JAXB</a> für das XML-Binding.

<h4>Semantische Konvention von POST, PUT und PATCH</h4>

<p>Die folgenden drei Methoden zeigen Implementierungen für die HTTP-Methoden POST, PUT und PATCH, die eine Ressource anlegen bzw. verändern. Dabei sind die im Folgenden erläuterten Unterschiede relevant.</p>
<ul>
<li><b>POST</b>: Beim Einfügen einer neuen Ressource wird deren eindeutige Id vom Server vergeben. Daher ist der Pfad <code>/accounts</code>. Die neu vergebene Id muss dem Client im Response-Body und/oder Response-Header mitgeteilt werden (Zeile 5).</li>
<pre><code class="language-java line-numbers">@POST
public Response postAccount(@NotNull Account account, @Context UriInfo uriInfo) {
	boolean validId = account.id > 0 && accounts.get(account.id) == null;
	if (!validId) {
		account.id = Account.nextId.getAndIncrement();
	}
	accounts.put(account.id, account);
	URI uri = uriInfo.getAbsolutePathBuilder().path(Integer.toString(account.id)).build(); // append new id to URI
	return Response.created(uri).entity(account).build(); // return code is 201
}</code></pre>

<li><b>PUT</b>: Es kann nur eine bereits bestehende Ressource ersetzt werden. Falls bisher keine Ressource unter der in der URI genannten Id zu finden ist, wird eine neue Ressource angelegt (s. Aufruf von <code>postAccount</code> in Zeile 6). Die bestehende Ressource wird vollständig ersetzt (Zeile 8).</li>
<pre><code class="language-java line-numbers">@PUT @Path("{id}")
public Response putAccount(@PathParam("id") int id, @NotNull Account account, @Context UriInfo uriInfo) {
	boolean exists = accounts.get(id) != null;
	account.id = id;
	if (!exists) {
		return postAccount(account, uriInfo);
	} else {
		accounts.put(id, account);
		return Response.ok(account).build(); // return code is 200
	}
}</code></pre>

<li><b>PATCH</b>: Es kann nur eine bereits bestehende Ressource aktualisiert werden. Falls keine Ressource unter der in der URI genannten Id zu finden ist, wird der entsprechende Status-Code 404 zurückgegeben (Zeile 6). Nur die im Body des HTTP-Request angegebenen Attribute der Ressource werden aktualisiert. Alle anderen Attribute bleiben unverändert erhalten (Zeilen 8-13).</li>
<pre><code class="language-java line-numbers">@PATCH @Path("{id}")
public Response patchAccount(@PathParam("id") int id, @NotNull Account patchedAccount) {
	Account account = accounts.get(id);
	boolean exists = account != null;
	if (!exists) {
		return Response.status(404).build(); // return code is 404
	} else {
		if (patchedAccount.owner != null) {
			account.owner = patchedAccount.owner;
		}
		if (patchedAccount.entries != null) {
			account.entries = patchedAccount.entries;
		}
		return Response.ok(account).build(); // return code is 200
	}
}</code></pre> 
</ul>

<span>Das Anlegen oder Verändern einer Ressource kann grundsätzlich fehlschlagen, falls die Validierung einzelner Attribute fehlschlägt. Exemplarisch werden einige typische Bedingungen für Validierungsschritte aufgezählt.</span>
<ul style="margin-bottom: 0">
<li>Ein Attribut darf nicht leer sein: "Es soll sich kein Anwender ohne Angabe einer E-Mail-Adresse registrieren."</li>
<li>Ein Attribut muss ein bestimmtes Format oder einen Wertebereich einhalten: "Die E-Mail-Adresse muss einem regulären Ausdruck entsprechen, u.a. ein @-Symbol enthalten."</li>
<li>Ein Attribut muss eindeutig sein: "Es sollen nicht mehrere Anwender mit derselben E-Mail-Adresse registriert sein."</li>
</ul>
<p>Zur Validierung bieten sich die <a href="https://beanvalidation.org/">Bean Validation</a>-Annotationen an. Im obigen Code-Beispiel wird exemplarisch bei jeder Methode die selbsterklärende Annotation <code>@NotNull</code> verwendet.</p>

<p>Der Unterschied zwischen PUT und PATCH soll noch anhand eines Beispiels verdeutlicht werden. Zur Vorbereitung wird über folgende POST-Anfrage ein neues Konto angelegt.

<pre><code class="language-json line-numbers"><!--​---[HTTP POST request]---
Request URL: http://localhost:8080/accounts
Content-type: application/json; charset=utf-8
{"owner": "A", "entries": [{"value": 10}, {"value": 20}]}

--​---[HTTP response 201]---
Content-type: application/json; charset=utf-8
{"id": 1, "owner": "A", "entries": [{"subject": null, "value": 10}, {"subject": null, "value": 20}]}
 --></code></pre> 

<p>Anschließend wird der Eigentümer des Kontos zunächst mittels einer PATCH-Anfrage verändert.</p>

<pre><code class="language-json line-numbers"><!--​---[HTTP PATCH request]---
Request URL: http://localhost:8080/accounts/1
Content-type: application/json; charset=utf-8
{"owner": "Patched"}

--​---[HTTP response 200]---
Content-type: application/json; charset=utf-8
{"id": 1, "owner": "Patched", "entries": [{"subject": null, "value": 10}, {"subject": null, "value": 20}]}
 --></code></pre> 

<p>Zum Vergleich wird der Eigentümer des Kontos danach mittels einer PUT-Anfrage verändert. Wie in der jeweiligen HTTP-Response zu erkennen ist, verändert PATCH die Kontoeinträge nicht, während PUT dafür sorgt, dass keine Kontoeinträge mehr existieren, da im Body des HTTP-Request auch keine übergeben worden sind.</p>
 
<pre><code class="language-json line-numbers"><!--​---[HTTP PUT request]---
Request URL: http://localhost:8080/accounts/1
Content-type: application/json; charset=utf-8
{"owner": "Changed"}

--​---[HTTP response 200]---
Content-type: application/json; charset=utf-8
{"id": 1, "owner": "Changed", "entries": null}
 --></code></pre> 

<h4>Repräsentation einer Ressource in einem Binärformat</h4>
<p>Das folgende Code-Beispiel zeigt zwei Ressourcen-Methoden, die beide auf GET-Anfragen bezüglich des Pfads <code>/users/&lt;id></code> reagieren, aber jeweils eine Ausgabe in unterschiedlichem Format erzeugen. Während die erste Methode <code>getUser</code> JSON oder XML als Ausgabe produziert, erzeugt die zweite Methode <code>getUserImage</code> eine Binärdatei, die das User-Profilbild enthält. Das JAX-RS-Framework wertet den HTTP-Header <code>Accept</code> aus, um zu entscheiden, welche der beiden Methoden ausgeführt werden soll. Dabei soll die Rückgabe in einem Format erfolgen, das möglichst der Anforderung des Client entspricht. Es kann allerdings sein, dass der Client z.B. <code>Accept: image/jpeg</code> sendet, aber eine Antwort mit <code>Content-Type: image/png</code> erhält, da das User-Profilbild serverseitig im PNG-Format vorliegt.</p>

<pre><code class="language-java line-numbers">@Path("/users")
public class UserResource {

    @GET @Path("{id}")
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public Response getUser(@PathParam("id") int id) {
        User user = getUserById(id);
        return Response.ok(user).build();
    }

    @GET @Path("{id}")
    @Produces({MediaType.APPLICATION_OCTET_STREAM, "image/png", "image/jpeg"})
    public Response getUserImage(@PathParam("id") int id) throws IOException {
        User user = getUserById(id);
        InputStream is = new ByteArrayInputStream(user.image);
        String type = URLConnection.guessContentTypeFromStream(is); // get MIME type
        String ext = type.substring(type.lastIndexOf("/") + 1); // get file extension
391
392
        return Response.ok(is).type(type)
          .header("Content-Disposition", "inline; filename=\"" + user.name + "." + ext + "\"").build();
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
    }

    private User getUserById(int id) {
        Optional&lt;User> user = UserService.queryById(id); // query user object from database
        if (user.isPresent()) { return user.get(); }
        throw new NotFoundException("User not found.");
    }
}</code></pre> 
 
<h4>Behandlung von Exceptions in JAX-RS</h4>
Wenn innerhalb einer Ressourcen-Methode eine unbehandelte <code>RuntimeException</code> (z.B. eine <code>NullPointerException</code>) auftritt, wird diese durch das JAX-RS-Framework gefangen und "verschluckt", so dass keine Ausgabe auf der Konsole oder im Log erscheint. JAX-RS sieht vor, dass der Entwickler gezielt sogenannte <i>ExceptionMapper</i> registriert, die auf spezifische Exceptions reagieren und diese auswerten, um eine aussagekräftige HTTP-Response an den aufrufenden Client zu senden. Das folgende Code-Beispiel zeigt exemplarisch drei verschiedene Implementierungen des <code>ExceptionMapper</code>-Interface. 
<ul>
<li>Der <code>ValidationExceptionMapper</code> behandelt Exceptions, die während der Validierung der <a href="https://beanvalidation.org/">Bean Validation</a>-Annotationen auftreten, d.h. jede <code>ConstraintValidationException</code>.</li>
<li>Der <code>JsonProcessingExceptionMapper</code> behandelt Exceptions, die während der Transformation von JSON-Input aus dem HTTP-Request-Body in Java-Objekte auftreten (z.B. unbekannte Attribute), d.h. jede <code>JsonProcessingException</code>.</li>
<li>Der <code>GenericExceptionMapper</code> reagiert auf jede beliebige <code>RuntimeException</code>. Falls es sich um eine konkrete <code>ClientException</code> (Status-Code 4xx) handelt, soll die Fehlermeldung lediglich an den Client weitergereicht werden. Falls nicht, wird der Exception-Stacktrace protokolliert und der generische Status-Code 500 ausgegeben.</li>
</ul>
 
<ul class="nav nav-tabs" id="rest-ex-tabs" role="tablist">
  <li class="nav-item"><a href="#rest-ex-tabs-validation" class="nav-link active" data-toggle="tab" role="tab">ValidationExceptionMapper</a></li>
  <li class="nav-item"><a href="#rest-ex-tabs-jsonproc" class="nav-link" data-toggle="tab" role="tab">JsonProcessingExceptionMapper</a></li>
  <li class="nav-item"><a href="#rest-ex-tabs-generic" class="nav-link" data-toggle="tab" role="tab">GenericExceptionMapper</a></li>  
</ul>
<div class="tab-content" id="rest-ex-tabs-content">
  <div class="tab-pane show active" id="rest-ex-tabs-validation" role="tabpanel">
	<pre><code class="language-java line-numbers">import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.ExceptionMapper;
import javax.validation.ConstraintViolationException;
// ...

@Provider
public class ValidationExceptionMapper implements ExceptionMapper&lt;ConstraintViolationException> {

    @Override
    public Response toResponse(ConstraintViolationException e) {
        ConstraintViolation violation = e.getConstraintViolations().stream().findFirst().get();
        String message = violation.getPropertyPath() + " " + violation.getMessage();
        return Response.status(Response.Status.BAD_REQUEST).entity(message).type("text/plain").build();
    }
}</code></pre>  
  </div>
  <div class="tab-pane" id="rest-ex-tabs-jsonproc" role="tabpanel">
	<pre><code class="language-java line-numbers">import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.ExceptionMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
// ...

@Provider
public class JsonProcessingExceptionMapper implements ExceptionMapper&lt;JsonProcessingException> {

    @Override
    public Response toResponse(JsonProcessingException e) {
        return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).type("text/plain").build();
    }
}</code></pre>  
  </div>
  <div class="tab-pane" id="rest-ex-tabs-generic" role="tabpanel">
	<pre><code class="language-java line-numbers">import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.ExceptionMapper;
// ...

@Provider
public class GenericExceptionMapper implements ExceptionMapper&lt;RuntimeException> {

    @Override
    public Response toResponse(RuntimeException e) {
        if (e instanceof ClientErrorException) {
            int status = ((ClientErrorException) e).getResponse().getStatus();
            return Response.status(status).entity(e.getMessage()).type("text/plain").build();
        }
        else {
            e.printStackTrace();
            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
        }
    }
}</code></pre>  
  </div>  
</div>  

<h4>Testen eines REST-Webservice</h4>
<p>Für automatisierte Unit-Tests bietet Jersey die Klasse <code>JerseyTest</code> (enthalten in der Bibliothek <a href="https://mvnrepository.com/artifact/org.glassfish.jersey.test-framework/jersey-test-framework-core">Jersey Test Framework Core</a>), die durch eine eigene Testklasse erweitert werden kann (Zeile 7). Das Jersey-Test-Framework startet automatisch einen HTTP-Server unter Port 9998, auf dem die zu testende Ressource bereitgestellt wird (Zeile 11) und anschließend die Testfälle ausgeführt werden. Mittels der Methode <code>target</code> der <code>JerseyTest</code>-Klasse können Anfragen an den REST-Webservice aus den Testfällen heraus erzeugt werden. Ansonsten gelten die üblichen Konvention für <a href="https://junit.org/junit5/">JUnit</a>-Tests. Dazu gehört u.a., dass der gemeinsame initiale Zustand für alle Testfälle durch eine Methode mit der Annotation <code>@BeforeEach</code> hergestellt wird (Zeilen 14-20). Das folgende Code-Beispiel zeigt zwei exemplarische Testfälle für PUT (Zeilen 22-30) und DELETE (Zeilen 32-39).</p>

<pre><code class="language-java line-numbers">import org.glassfish.jersey.test.JerseyTest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
// ...

@TestInstance(Lifecycle.PER_CLASS)
public class AccountResourceTest extends JerseyTest {

    @Override
    protected Application configure() {
        return new ResourceConfig(AccountResource.class);
    }

    @BeforeEach
    public void prepareTest() {
        target().path("accounts").request().delete(); // delete all accounts
        String a = "{\"owner\": \"A\", \"entries\": [{\"value\": 20}, {\"value\": -10}]}";
        target("accounts/1").request().put(Entity.json(a)); // put a test account with id = 1
		// ...
    }	
	
    @Test
    public void testPutAccount() {
        String a = "{\"owner\": \"Changed\"}";
        Response res = target("accounts/1").request().put(Entity.json(a));
        assertEquals(200, res.getStatus());
        Account account = res.readEntity(Account.class);
        assertEquals("Changed", account.owner);
        assertEquals(0, account.balance());		
    }

    @Test
    public void testDeleteAccount() {
        Response res = target("accounts/1").request().delete();
        assertEquals(204, res.getStatus());

        res = target("accounts/1").request().get(Response.class);
        assertEquals(404, res.getStatus());
    }

	// ...
}</code></pre> 

<p>Zur Entwicklungszeit ist es wichtig, die REST-API einfach stichprobenartig testen zu können. Die erstellten Testfälle sollen zum Austausch mit anderen Entwicklern und zur Versionierung gespeichert werden können, und sie sollen wiederholt und automatisch ausführbar sein. Dazu gibt es mehrere, relativ ähnliche Werkzeuge, die entweder als Desktop-Anwendung oder als Browser-Plugin installiert werden können – z.B. <a href="https://www.getpostman.com/">Postman</a>, <a href="https://insomnia.rest/">Insomnia</a> und <a href="https://advancedrestclient.com/">Advanced REST Client</a>. Alternativ ist es auch möglich die REST-API direkt aus der Entwicklungsumgebung (IDE) zu testen. Die beiden folgenden Videos zeigen kurz, wie die entwickelte REST-API zur Kontenverwaltung exemplarisch mittels Postman und IntelliJ IDEA getestet werden können.</p>

<ul class="nav nav-tabs" id="rest-client-video-tabs" role="tablist">
  <li class="nav-item"><a href="#rest-client-video-tabs-postman" class="nav-link active" data-toggle="tab" role="tab">REST-Client Postman</a></li>
  <li class="nav-item"><a href="#rest-client-video-tabs-intellij" class="nav-link" data-toggle="tab" role="tab">REST-Client in IntelliJ IDEA</a></li> 
</ul>
<div class="tab-content" id="rest-client-video-tabs-content">
  <div class="tab-pane show active" id="rest-client-video-tabs-postman" role="tabpanel">
	<video controls><source src="media/Postman.mp4" type="video/mp4"></video>
  </div>
  <div class="tab-pane" id="rest-client-video-tabs-intellij" role="tabpanel">
	<video controls><source src="media/IntelliJ_IDEA_REST_Client.mp4" type="video/mp4"></video>
  </div>
</div>

<h4>JWT (JSON Web Token)</h4>

<p>REST-Webservices sind per Konvention zustandslos, d.h. auf dem Server werden keine Sessions für die aktuell verbundenen Clients verwalten. Demzufolge muss jeder Client selbst seine Session verwaltet und mit jedem HTTP-Request ein Token o.ä. an den Server senden, über das er authentifiziert und für den Zugriff autorisiert werden kann. Eine standardisierte Möglichkeit zur Erzeugung derartiger Tokens findet sich in <a href="https://tools.ietf.org/html/rfc7519">RFC 7519</a>, in dem sogenannte <a href="https://jwt.io/"><i>JSON Web Token (JWT)</i></a> eingeführt werden.</p>

<div class="cite"><a href="https://tools.ietf.org/html/rfc7519">"JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties.  The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted." (Internet Engineering Task Force)</a></div>

<p>Der Kommunikationsablauf zwischen Client und Server bei Verwendung von JWT wird in der folgenden Abbildung dargestellt.</p>

<img src="media/distributed_jwt.png" style="width:800px">
<label>Ablauf der Kommunikation bei Authentifizierung mittels JWT</label>

<ol>
<li>Der Anwender authentifiziert sich beim Server, z.B. in dem er seinen Usernamen und sein Passwort in ein Login-Formular einträgt und dieses absendet.</li>
<li>Der Server generiert ein individuelles Token für den Client. Zur Signatur des Token wird ein <i>Secret</i> verwendet, das ausschließlich dem Erzeuger des Token bekannt ist. Das Token besteht aus 3 Teilen: dem Header, dem eigentlichen Inhalt genannt <i>Payload</i> und der Signatur.
<ul>
<li><b>Header</b>: Der Header enthält die Information, welcher Hash-/Verschlüsselungsalgorithmus eingesetzt wird, z.B. <code style="color:#fb015b">{"alg": "HS256"}</code>.</li>
<li><b>Payload</b>: Der Payload enthält ein gültiges JSON-Objekt, das den sogenannten <i>Claim</i> abbildet. Der Claim drückt eine Anwenderrolle aus, die der Client mittels des Token für sich beansprucht und die ihm bei gültiger Signatur auch zugestanden wird. In dem unten dargestellten Screenshot der Webseite <a href="https://jwt.io/">jwt.io</a> ist der Claim exemplarisch <code style="color:#d63aff">{"sub": 123, "name": "John Doe", "iat": 1612083600}</code>. Es gibt einige verbreitete Claim-Attribute, die im RFC 7519 genannt werden, wie z.B. <i>Subject (sub)</i>, <i>Issued At (iat)</i> und <i>Expiration Time (exp)</i>.</li>
<li><b>Signatur</b>: Während Header und Payload lediglich Base64-encodiert werden und damit in Klartext vorliegen, enthält die Signatur ein verschlüsseltes <i>Secret</i>. Dieses Geheimnis ist bei einem Hash-Algorithmus wie <a href="https://en.wikipedia.org/wiki/HMAC">HMAC</a> mit <a href="https://en.wikipedia.org/wiki/SHA-2">SHA-256</a> (kurz HS256) eine entsprechend lange Zeichenkette (z.B. 32 Zeichen bei SHA-256). Alternativ besteht das Geheimnis in einem privaten Schlüssel zur asymmetrischen Verschlüsselung wie bei <a href="https://en.wikipedia.org/wiki/RSA_(cryptosystem)">RSA</a> mit SHA-256 (kurz RS256). Das Geheimnis bildet zusammen mit dem Base64-encodierten Header und dem ebenfalls Base64-encodierten Payload die Eingabe für den gewählten Hash-/Verschlüsselungsalgorithmus. Dadurch ist es dem Client nicht möglich Header oder Payload zu manipulieren, da sich selbst bei einer minimalen Änderung eine andere (ungültige) Signatur ergibt.</li>
</ul>
</li>
<li>Der Server sendet das Token an den Client, der es sicher vor dem Zugriff von Dritten verwahrt. Das Token mit Header <code style="color:#fb015b">{"alg": "HS256"}</code>, Payload <code style="color:#d63aff">{"sub": 123, "name": "John Doe", "iat": 1612083600}</code> und Secret <span style="color:#1fc168">my-secret</span> wäre z.B. <span style="color:#fb015b">eyJhbGciOiJIUzI1NiJ9</span>.<span style="color:#d63aff">eyJzdWIiOjEyMywibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNjEyMDgzNjAwfQ</span>.<span style="color:#1fc168">FQVhKVWAzvW-Pje0FlB30ITulrRwcI9Mm8HpOinMYYc</span>.</li>
<li>Der Client sendet das Token mit jedem weiteren HTTP-Request im HTTP-Header <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization"><code>Authorization</code></a> an den Server.</li>
<li>Der Server verifiziert das Token, bevor er den Zugriff auf die angeforderte Ressource zulässt. Falls es sich um kein gültiges Token handelt, sollte der Status-Code <i>Unauthorized (401)</i> zurückgegeben werden. Falls es sich zwar um ein gültiges Token handelt, aber der authentifizierte Anwender nicht über die notwendigen Rechte verfügt, um auf die Ressource zuzugreifen, sollte der Status-Code <i>Forbidden (403)</i> zurückgegeben werden. Das Token kann durch ein <i>Expiration Time</i>-Attribut in seiner Gültigkeit eingeschränkt werden. Aufgrund der prinzipiellen Zustandslosigkeit der REST-API kann der Server ein einmal ausgestelltes Token aber nicht sofort für ungültig erklären (z.B. Logout erzwingen). Als Workaround könnte ein Token einer serverseitigen Blacklist hinzugefügt werden, was aber der Idee der Zustandslosigkeit widerspricht.</li>
<li>Bei autorisiertem Zugriff auf die Ressource wird die eigentliche Anfrage mit einer entsprechenden HTTP-Response bedient.</li>
</ol>

<a href="media/distributed_jwtio.png"><img src="media/distributed_jwtio.png" style="width:500px"></a>
<label>Interaktive Einführung in JWT auf <a href="https://jwt.io/">jwt.io</a></label>

<p>Es existieren diverse JWT-Implementierungen für alle gängigen Programmiersprachen. Eine gute Übersicht über die zur Verfügung stehenden JWT-Bibliotheken sowie eine interaktive Einführung findet sich auf <a href="https://jwt.io/">jwt.io</a>. Das folgende Code-Beispiel verwendet die JWT-Bibliothek <a href="https://connect2id.com/products/nimbus-jose-jwt">Nimbus JOSE+JWT</a> für Java.</p>

<ul class="nav nav-tabs" id="rest-jwt-tabs" role="tablist">
  <li class="nav-item"><a href="#rest-jwt-tabs-auth-resource" class="nav-link active" data-toggle="tab" role="tab">AuthenticationResource</a></li>
  <li class="nav-item"><a href="#rest-jwt-tabs-auth-filter" class="nav-link" data-toggle="tab" role="tab">AuthenticationFilter</a></li>
  <li class="nav-item"><a href="#rest-jwt-tabs-secured-resource" class="nav-link" data-toggle="tab" role="tab">UserResource</a></li>  
  <li class="nav-item"><a href="#rest-jwt-tabs-claim" class="nav-link" data-toggle="tab" role="tab">JWTClaim</a></li>  
</ul>
<div class="tab-content" id="rest-jwt-tabs-content">
  <div class="tab-pane show active" id="rest-jwt-tabs-auth-resource" role="tabpanel">
	<pre><code class="language-java line-numbers">@Path("/login")
public class AuthenticationResource {

    @POST
    @Produces(MediaType.TEXT_PLAIN)
    public String login(User userClaim) {
        Optional&lt;User> user = UserService.queryByCredentials(userClaim.name, userClaim.passwordHash); // lookup user in database
        if (user.isPresent()) {
            try {
                // map claim object to JSON
                JWTClaim claim = new JWTClaim(String.valueOf(user.get().id), user.get().name);
                String claimJson = new ObjectMapper().writeValueAsString(claim);

                // create JWS object with claim as payload
                JWSObject jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.HS256), new Payload(claimJson));

                // use a secret key for HS256 and sign the JWS object using HMAC
                byte[] secret = AuthenticationFilter.SECRET.getBytes();
                jwsObject.sign(new MACSigner(secret));

                // provide token to the client
                return Response.ok( jwsObject.serialize() ).build();
                
            } catch (JsonProcessingException | JOSEException e) {
                throw new RuntimeException(e.getMessage());
            }
        }
        return Response.status(401).entity("Login failed").build();
    }
}</code></pre>  
  </div>
  <div class="tab-pane" id="rest-jwt-tabs-auth-filter" role="tabpanel">
	<pre><code class="language-java line-numbers">import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.ext.Provider;
// ...
	
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {

    public static final String SECRET = "my-very-private-secret:32-symbols=256-bit";
    static final String TOKEN_TYPE = "Bearer";

    @NameBinding @Retention(RetentionPolicy.RUNTIME)
    public @interface Secured { }

    @Override
    public void filter(ContainerRequestContext request) {
        // validate HTTP authorization header
        String authHeader = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (authHeader == null || !authHeader.startsWith(TOKEN_TYPE + " ")) {
            request.abortWith(Response.status(401).entity("JWT not found in HTTP header").build());
        }
        // extract the token from the HTTP authorization header
        String jwt = authHeader.substring(TOKEN_TYPE.length()).trim();

        // validate JWT signature
        try {
            JWSObject jwsObject = JWSObject.parse(jwt);
            JWSVerifier verifier = new MACVerifier(SECRET);
            if (jwsObject.verify(verifier)) {
                String payload = jwsObject.getPayload().toString();
                JWTClaim claim = new ObjectMapper().readValue(payload, JWTClaim.class);
                System.out.println("User verified: " + claim.name);
                return;
            }
            else {
                request.abortWith(Response.status(401).entity("Invalid JWT").build());
            }
        } catch (ParseException | JOSEException | IOException e) {
            request.abortWith(Response.status(400).entity(e.getMessage()).build());
        }
    }
}</code></pre>  
  </div>
  <div class="tab-pane" id="rest-jwt-tabs-secured-resource" role="tabpanel">
	<pre><code class="language-java line-numbers">@Path("/users")
public class UserResource {

	@Secured
    @GET
    public Collection&lt;User> getUsers() {
        return UserService.queryAllUsers();
    }
	
	// ...
}</code></pre>  
  </div>
  <div class="tab-pane" id="rest-jwt-tabs-claim" role="tabpanel">
	<pre><code class="language-java line-numbers">public class JWTClaim {

    // registered JWT claim names, see https://tools.ietf.org/html/rfc7519#section-4.1
    String sub; // subject
    String name;
    Date iat; // issued at
    Date exp; // expiration time

    public JWTClaim(String sub, String name) {
        this.sub = sub;
        this.name = name;
        Calendar cal = Calendar.getInstance();
        this.iat = cal.getTime();
        cal.add(Calendar.HOUR, 1); // tokens are valid for 1 hour
        this.exp = cal.getTime();
    }
}</code></pre>  
  </div>    
</div>

<ul>
<li><code>AuthenticationResource</code>: Diese Ressourcen-Klasse implementiert eine Methode zur Authentifizierung, die über den Pfad <code>/login</code> erreichbar ist. Die Ausgabe ist bei erfolgreichem Login ein JSON Web Token (Zeile 22), bei nicht erfolgreichem Login ein entsprechender Fehler (Zeilen 25+28). Falls die übergebenen Credentials stimmen (Zeilen 7-8), werden folgende Schritte ausgeführt:
<ul>
<li>Zusammenstellen des Claim-Objekts inkl. <i>Expiration Time</i> (Zeilen 11-12).</li>
<li>Erzeugen eines JWS-Objekts (s. <a href="https://tools.ietf.org/html/rfc7515">RFC 7515: JSON Web Signature</a>), das den Claim als Payload enthält und den verwendeten Verschlüsselungsalgorithmus angibt (Zeile 15).</li>
<li>Verschlüsseln des JWS-Objekts mit einem <i>Secret</i> (Zeilen 18-19).</li>
<li>Serialisieren des JWS-Objekts in das JWT-Format (Zeile 22).</li>
</ul>
</li>
<li><code>AuthenticationFilter</code>: Das JWT, das der Client bei weiteren Anfragen im HTTP-Header mitsendet, soll bei gesicherten Ressourcen-Methoden geprüft werden. Die Sicherung einer Ressourcen-Methode erfolgt über einen eigene Annotation, die hier <code>@Secured</code> heißen soll und in den Zeilen 12-13 der Klasse <code>AuthenticationFilter</code> deklariert wird. Diese Klasse implementiert das Interface <code>ContainerRequestFilter</code> und funktioniert ihrem Namen entsprechend als Filter vor jedem Aufruf einer Methode mit der Annotation <code>@Secured</code>. Die Verifikation des JWT erfolgt in drei Schritten: 
<ul>
<li>Zunächst wird das JWT als sogenanntes <a href="https://www.iana.org/assignments/http-authschemes/">Bearer Token</a> aus dem HTTP-Header extrahiert (Zeilen 17-23).</li>
<li>Anschließend wird aus dem JWT wieder ein JWS-Objekt erzeugt (Zeile 27) und dieses mit demselben <i>Secret</i> verifiziert, das auch zur Signatur verwendet worden ist (Zeilen 28-29).</li>
<li>Nachdem das JWS-Objekt verifiziert ist, kann der Claim inhaltlich ausgewertet werden. Ist der Anwender berechtigt den Ressourcenzugriff durchzuführen? Ist das Token ggf. schon abgelaufen? Im Beispiel wird der Filter in Zeile 33 erfolgreich verlassen und der Ressourcenzugriff erlaubt.</li>
</ul>
</li>
<li><code>UserResource</code>: Die Klasse enthält eine Ressourcen-Methode, die exemplarisch mit <code>@Secured</code> annotiert ist, um den Zugriff auf diese Methode mittels der <code>AuthenticationFilter</code>-Klasse abzusichern.</li>
</ul>