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
:
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from typing_extensions import Annotated
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
Tipp
Bevorzugen Sie die Annotated
-Version, falls möglich.
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}
Ausführen¶
Info
Um hochgeladene Dateien zu empfangen, installieren Sie zuerst python-multipart
.
Z. B. pip install python-multipart
.
Das, weil OAuth2 „Formulardaten“ zum Senden von username
und password
verwendet.
Führen Sie das Beispiel aus mit:
$ uvicorn main:app --reload
<span style="color: green;">INFO</span>: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Überprüfen¶
Gehen Sie zu der interaktiven Dokumentation unter: http://127.0.0.1:8000/docs.
Sie werden etwa Folgendes sehen:
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):
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 daspassword
im Frontend ein und drücktEnter
. - Das Frontend (das im Browser des Benutzers läuft) sendet diesen
username
und daspassword
an eine bestimmte URL in unserer API (deklariert mittokenUrl="token"
). - Die API überprüft den
username
und daspassword
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 WertBearer
plus dem Token. - Wenn der Token
foobar
enthielte, wäre der Inhalt desAuthorization
-Headers:Bearer foobar
.
FastAPIs 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.
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from typing_extensions import Annotated
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
Tipp
Bevorzugen Sie die Annotated
-Version, falls möglich.
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}
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, 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:
oauth2_scheme(some, parameters)
Es kann also mit Depends
verwendet werden.
Verwendung¶
Jetzt können Sie dieses oauth2_scheme
als Abhängigkeit Depends
übergeben.
from typing import Annotated
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from typing_extensions import Annotated
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}
Tipp
Bevorzugen Sie die Annotated
-Version, falls möglich.
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
@app.get("/items/")
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}
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.
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:
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.