committed by
GitHub
1 changed files with 233 additions and 0 deletions
@ -0,0 +1,233 @@ |
|||||
|
# Sicherheit – Erste Schritte |
||||
|
|
||||
|
Stellen wir uns vor, dass Sie Ihre **Backend**-API auf einer Domain haben. |
||||
|
|
||||
|
Und Sie haben ein **Frontend** auf einer anderen Domain oder in einem anderen Pfad derselben Domain (oder in einer mobilen Anwendung). |
||||
|
|
||||
|
Und Sie möchten eine Möglichkeit haben, dass sich das Frontend mithilfe eines **Benutzernamens** und eines **Passworts** beim Backend authentisieren kann. |
||||
|
|
||||
|
Wir können **OAuth2** verwenden, um das mit **FastAPI** zu erstellen. |
||||
|
|
||||
|
Aber ersparen wir Ihnen die Zeit, die gesamte lange Spezifikation zu lesen, nur um die kleinen Informationen zu finden, die Sie benötigen. |
||||
|
|
||||
|
Lassen Sie uns die von **FastAPI** bereitgestellten Tools verwenden, um Sicherheit zu gewährleisten. |
||||
|
|
||||
|
## Wie es aussieht |
||||
|
|
||||
|
Lassen Sie uns zunächst einfach den Code verwenden und sehen, wie er funktioniert, und dann kommen wir zurück, um zu verstehen, was passiert. |
||||
|
|
||||
|
## `main.py` erstellen |
||||
|
|
||||
|
Kopieren Sie das Beispiel in eine Datei `main.py`: |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python |
||||
|
{!> ../../../docs_src/security/tutorial001_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python |
||||
|
{!> ../../../docs_src/security/tutorial001_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ nicht annotiert" |
||||
|
|
||||
|
!!! tip "Tipp" |
||||
|
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
||||
|
|
||||
|
```Python |
||||
|
{!> ../../../docs_src/security/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
## Ausführen |
||||
|
|
||||
|
!!! info |
||||
|
Um hochgeladene Dateien zu empfangen, installieren Sie zuerst <a href="https://andrew-d.github.io/python-multipart/" class="external-link" target="_blank">`python-multipart`</a>. |
||||
|
|
||||
|
Z. B. `pip install python-multipart`. |
||||
|
|
||||
|
Das, weil **OAuth2** „Formulardaten“ zum Senden von `username` und `password` verwendet. |
||||
|
|
||||
|
Führen Sie das Beispiel aus mit: |
||||
|
|
||||
|
<div class="termy"> |
||||
|
|
||||
|
```console |
||||
|
$ uvicorn main:app --reload |
||||
|
|
||||
|
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) |
||||
|
``` |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
## Überprüfen |
||||
|
|
||||
|
Gehen Sie zu der interaktiven Dokumentation unter: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>. |
||||
|
|
||||
|
Sie werden etwa Folgendes sehen: |
||||
|
|
||||
|
<img src="/img/tutorial/security/image01.png"> |
||||
|
|
||||
|
!!! check "Authorize-Button!" |
||||
|
Sie haben bereits einen glänzenden, neuen „Authorize“-Button. |
||||
|
|
||||
|
Und Ihre *Pfadoperation* hat in der oberen rechten Ecke ein kleines Schloss, auf das Sie klicken können. |
||||
|
|
||||
|
Und wenn Sie darauf klicken, erhalten Sie ein kleines Anmeldeformular zur Eingabe eines `username` und `password` (und anderer optionaler Felder): |
||||
|
|
||||
|
<img src="/img/tutorial/security/image02.png"> |
||||
|
|
||||
|
!!! note "Hinweis" |
||||
|
Es spielt keine Rolle, was Sie in das Formular eingeben, es wird noch nicht funktionieren. Wir kommen dahin. |
||||
|
|
||||
|
Dies ist natürlich nicht das Frontend für die Endbenutzer, aber es ist ein großartiges automatisches Tool, um Ihre gesamte API interaktiv zu dokumentieren. |
||||
|
|
||||
|
Es kann vom Frontend-Team verwendet werden (das auch Sie selbst sein können). |
||||
|
|
||||
|
Es kann von Anwendungen und Systemen Dritter verwendet werden. |
||||
|
|
||||
|
Und es kann auch von Ihnen selbst verwendet werden, um dieselbe Anwendung zu debuggen, zu prüfen und zu testen. |
||||
|
|
||||
|
## Der `password`-Flow |
||||
|
|
||||
|
Lassen Sie uns nun etwas zurückgehen und verstehen, was das alles ist. |
||||
|
|
||||
|
Der `password`-„Flow“ ist eine der in OAuth2 definierten Wege („Flows“) zur Handhabung von Sicherheit und Authentifizierung. |
||||
|
|
||||
|
OAuth2 wurde so konzipiert, dass das Backend oder die API unabhängig vom Server sein kann, der den Benutzer authentifiziert. |
||||
|
|
||||
|
In diesem Fall handhabt jedoch dieselbe **FastAPI**-Anwendung sowohl die API als auch die Authentifizierung. |
||||
|
|
||||
|
Betrachten wir es also aus dieser vereinfachten Sicht: |
||||
|
|
||||
|
* Der Benutzer gibt den `username` und das `password` im Frontend ein und drückt `Enter`. |
||||
|
* Das Frontend (das im Browser des Benutzers läuft) sendet diesen `username` und das `password` an eine bestimmte URL in unserer API (deklariert mit `tokenUrl="token"`). |
||||
|
* Die API überprüft den `username` und das `password` und antwortet mit einem „Token“ (wir haben davon noch nichts implementiert). |
||||
|
* Ein „Token“ ist lediglich ein String mit einem Inhalt, den wir später verwenden können, um diesen Benutzer zu verifizieren. |
||||
|
* Normalerweise läuft ein Token nach einiger Zeit ab. |
||||
|
* Daher muss sich der Benutzer irgendwann später erneut anmelden. |
||||
|
* Und wenn der Token gestohlen wird, ist das Risiko geringer. Es handelt sich nicht um einen dauerhaften Schlüssel, der (in den meisten Fällen) für immer funktioniert. |
||||
|
* Das Frontend speichert diesen Token vorübergehend irgendwo. |
||||
|
* Der Benutzer klickt im Frontend, um zu einem anderen Abschnitt der Frontend-Web-Anwendung zu gelangen. |
||||
|
* Das Frontend muss weitere Daten von der API abrufen. |
||||
|
* Es benötigt jedoch eine Authentifizierung für diesen bestimmten Endpunkt. |
||||
|
* Um sich also bei unserer API zu authentifizieren, sendet es einen Header `Authorization` mit dem Wert `Bearer` plus dem Token. |
||||
|
* Wenn der Token `foobar` enthielte, wäre der Inhalt des `Authorization`-Headers: `Bearer foobar`. |
||||
|
|
||||
|
## **FastAPI**s `OAuth2PasswordBearer` |
||||
|
|
||||
|
**FastAPI** bietet mehrere Tools auf unterschiedlichen Abstraktionsebenen zur Implementierung dieser Sicherheitsfunktionen. |
||||
|
|
||||
|
In diesem Beispiel verwenden wir **OAuth2** mit dem **Password**-Flow und einem **Bearer**-Token. Wir machen das mit der Klasse `OAuth2PasswordBearer`. |
||||
|
|
||||
|
!!! info |
||||
|
Ein „Bearer“-Token ist nicht die einzige Option. |
||||
|
|
||||
|
Aber es ist die beste für unseren Anwendungsfall. |
||||
|
|
||||
|
Und es ist wahrscheinlich auch für die meisten anderen Anwendungsfälle die beste, es sei denn, Sie sind ein OAuth2-Experte und wissen genau, warum es eine andere Option gibt, die Ihren Anforderungen besser entspricht. |
||||
|
|
||||
|
In dem Fall gibt Ihnen **FastAPI** ebenfalls die Tools, die Sie zum Erstellen brauchen. |
||||
|
|
||||
|
Wenn wir eine Instanz der Klasse `OAuth2PasswordBearer` erstellen, übergeben wir den Parameter `tokenUrl`. Dieser Parameter enthält die URL, die der Client (das Frontend, das im Browser des Benutzers ausgeführt wird) verwendet, wenn er den `username` und das `password` sendet, um einen Token zu erhalten. |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="8" |
||||
|
{!> ../../../docs_src/security/tutorial001_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="7" |
||||
|
{!> ../../../docs_src/security/tutorial001_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ nicht annotiert" |
||||
|
|
||||
|
!!! tip "Tipp" |
||||
|
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
||||
|
|
||||
|
```Python hl_lines="6" |
||||
|
{!> ../../../docs_src/security/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
!!! tip "Tipp" |
||||
|
Hier bezieht sich `tokenUrl="token"` auf eine relative URL `token`, die wir noch nicht erstellt haben. Da es sich um eine relative URL handelt, entspricht sie `./token`. |
||||
|
|
||||
|
Da wir eine relative URL verwenden, würde sich das, wenn sich Ihre API unter `https://example.com/` befindet, auf `https://example.com/token` beziehen. Wenn sich Ihre API jedoch unter `https://example.com/api/v1/` befände, würde es sich auf `https://example.com/api/v1/token` beziehen. |
||||
|
|
||||
|
Die Verwendung einer relativen URL ist wichtig, um sicherzustellen, dass Ihre Anwendung auch in einem fortgeschrittenen Anwendungsfall, wie [hinter einem Proxy](../../advanced/behind-a-proxy.md){.internal-link target=_blank}, weiterhin funktioniert. |
||||
|
|
||||
|
Dieser Parameter erstellt nicht diesen Endpunkt / diese *Pfadoperation*, sondern deklariert, dass die URL `/token` diejenige sein wird, die der Client verwenden soll, um den Token abzurufen. Diese Information wird in OpenAPI und dann in den interaktiven API-Dokumentationssystemen verwendet. |
||||
|
|
||||
|
Wir werden demnächst auch die eigentliche Pfadoperation erstellen. |
||||
|
|
||||
|
!!! info |
||||
|
Wenn Sie ein sehr strenger „Pythonista“ sind, missfällt Ihnen möglicherweise die Schreibweise des Parameternamens `tokenUrl` anstelle von `token_url`. |
||||
|
|
||||
|
Das liegt daran, dass FastAPI denselben Namen wie in der OpenAPI-Spezifikation verwendet. Sodass Sie, wenn Sie mehr über eines dieser Sicherheitsschemas herausfinden möchten, den Namen einfach kopieren und einfügen können, um weitere Informationen darüber zu erhalten. |
||||
|
|
||||
|
Die Variable `oauth2_scheme` ist eine Instanz von `OAuth2PasswordBearer`, aber auch ein „Callable“. |
||||
|
|
||||
|
Es könnte wie folgt aufgerufen werden: |
||||
|
|
||||
|
```Python |
||||
|
oauth2_scheme(some, parameters) |
||||
|
``` |
||||
|
|
||||
|
Es kann also mit `Depends` verwendet werden. |
||||
|
|
||||
|
### Verwendung |
||||
|
|
||||
|
Jetzt können Sie dieses `oauth2_scheme` als Abhängigkeit `Depends` übergeben. |
||||
|
|
||||
|
=== "Python 3.9+" |
||||
|
|
||||
|
```Python hl_lines="12" |
||||
|
{!> ../../../docs_src/security/tutorial001_an_py39.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+" |
||||
|
|
||||
|
```Python hl_lines="11" |
||||
|
{!> ../../../docs_src/security/tutorial001_an.py!} |
||||
|
``` |
||||
|
|
||||
|
=== "Python 3.8+ nicht annotiert" |
||||
|
|
||||
|
!!! tip "Tipp" |
||||
|
Bevorzugen Sie die `Annotated`-Version, falls möglich. |
||||
|
|
||||
|
```Python hl_lines="10" |
||||
|
{!> ../../../docs_src/security/tutorial001.py!} |
||||
|
``` |
||||
|
|
||||
|
Diese Abhängigkeit stellt einen `str` bereit, der dem Parameter `token` der *Pfadoperation-Funktion* zugewiesen wird. |
||||
|
|
||||
|
**FastAPI** weiß, dass es diese Abhängigkeit verwenden kann, um ein „Sicherheitsschema“ im OpenAPI-Schema (und der automatischen API-Dokumentation) zu definieren. |
||||
|
|
||||
|
!!! info "Technische Details" |
||||
|
**FastAPI** weiß, dass es die Klasse `OAuth2PasswordBearer` (deklariert in einer Abhängigkeit) verwenden kann, um das Sicherheitsschema in OpenAPI zu definieren, da es von `fastapi.security.oauth2.OAuth2` erbt, das wiederum von `fastapi.security.base.SecurityBase` erbt. |
||||
|
|
||||
|
Alle Sicherheits-Werkzeuge, die in OpenAPI integriert sind (und die automatische API-Dokumentation), erben von `SecurityBase`, so weiß **FastAPI**, wie es sie in OpenAPI integrieren muss. |
||||
|
|
||||
|
## Was es macht |
||||
|
|
||||
|
FastAPI wird im Request nach diesem `Authorization`-Header suchen, prüfen, ob der Wert `Bearer` plus ein Token ist, und den Token als `str` zurückgeben. |
||||
|
|
||||
|
Wenn es keinen `Authorization`-Header sieht, oder der Wert keinen `Bearer`-Token hat, antwortet es direkt mit einem 401-Statuscode-Error (`UNAUTHORIZED`). |
||||
|
|
||||
|
Sie müssen nicht einmal prüfen, ob der Token existiert, um einen Fehler zurückzugeben. Seien Sie sicher, dass Ihre Funktion, wenn sie ausgeführt wird, ein `str` in diesem Token enthält. |
||||
|
|
||||
|
Sie können das bereits in der interaktiven Dokumentation ausprobieren: |
||||
|
|
||||
|
<img src="/img/tutorial/security/image03.png"> |
||||
|
|
||||
|
Wir überprüfen im Moment noch nicht die Gültigkeit des Tokens, aber das ist bereits ein Anfang. |
||||
|
|
||||
|
## Zusammenfassung |
||||
|
|
||||
|
Mit nur drei oder vier zusätzlichen Zeilen haben Sie also bereits eine primitive Form der Sicherheit. |
Loading…
Reference in new issue