Ana içeriğe geç

Güvenlik - İlk Adımlar

🌐 Translation by AI and humans

This translation was made by AI guided by humans. 🤝

It could have mistakes of misunderstanding the original meaning, or looking unnatural, etc. 🤖

You can improve this translation by helping us guide the AI LLM better.

English version

backend API’nizin bir domain’de olduğunu düşünelim.

Ve başka bir domain’de ya da aynı domain’in farklı bir path’inde (veya bir mobil uygulamada) bir frontend’iniz var.

Ve frontend’in, username ve password kullanarak backend ile kimlik doğrulaması yapabilmesini istiyorsunuz.

Bunu FastAPI ile OAuth2 kullanarak oluşturabiliriz.

Ama ihtiyacınız olan küçük bilgi parçalarını bulmak için uzun spesifikasyonun tamamını okuma zahmetine girmeyelim.

Güvenliği yönetmek için FastAPI’nin sunduğu araçları kullanalım.

Nasıl Görünüyor

Önce kodu kullanıp nasıl çalıştığına bakalım, sonra neler olup bittiğini anlamak için geri döneriz.

main.py Oluşturun

Örneği main.py adlı bir dosyaya kopyalayın:

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}
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

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}

Çalıştırın

Bilgi

python-multipart paketi, pip install "fastapi[standard]" komutunu çalıştırdığınızda FastAPI ile birlikte otomatik olarak kurulur.

Ancak pip install fastapi komutunu kullanırsanız, python-multipart paketi varsayılan olarak dahil edilmez.

Elle kurmak için bir virtual environment oluşturduğunuzdan, onu aktive ettiğinizden emin olun ve ardından şununla kurun:

$ pip install python-multipart

Bunun nedeni, OAuth2’nin username ve password göndermek için "form data" kullanmasıdır.

Örneği şu şekilde çalıştırın:

$ fastapi dev main.py

<span style="color: green;">INFO</span>:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

Kontrol Edin

Etkileşimli dokümantasyona gidin: http://127.0.0.1:8000/docs.

Şuna benzer bir şey göreceksiniz:

Authorize butonu!

Artık parıl parıl yeni bir "Authorize" butonunuz var.

Ayrıca path operation’ınızın sağ üst köşesinde tıklayabileceğiniz küçük bir kilit simgesi de bulunuyor.

Ve ona tıklarsanız, username ve password (ve diğer opsiyonel alanları) girebileceğiniz küçük bir yetkilendirme formu görürsünüz:

Not

Formda ne yazdığınızın önemi yok; şimdilik çalışmayacak. Ama birazdan oraya da geleceğiz.

Bu, elbette son kullanıcılar için bir frontend değil; ancak tüm API’nizi etkileşimli şekilde belgelemek için harika bir otomatik araçtır.

Frontend ekibi tarafından kullanılabilir (bu ekip siz de olabilirsiniz).

Üçüncü taraf uygulamalar ve sistemler tarafından kullanılabilir.

Ve aynı uygulamayı debug etmek, kontrol etmek ve test etmek için sizin tarafınızdan da kullanılabilir.

password Flow

Şimdi biraz geri dönüp bunların ne olduğuna bakalım.

password "flow"u, OAuth2’de güvenlik ve authentication’ı yönetmek için tanımlanmış yöntemlerden ("flow"lardan) biridir.

OAuth2, backend’in veya API’nin, kullanıcıyı authenticate eden server’dan bağımsız olabilmesi için tasarlanmıştır.

Ancak bu örnekte, aynı FastAPI uygulaması hem API’yi hem de authentication’ı yönetecek.

O yüzden basitleştirilmiş bu bakış açısından üzerinden geçelim:

  • Kullanıcı frontend’de username ve password yazar ve Enter’a basar.
  • Frontend (kullanıcının browser’ında çalışır), bu username ve password değerlerini API’mizdeki belirli bir URL’ye gönderir (tokenUrl="token" ile tanımlanan).
  • API, username ve password değerlerini kontrol eder ve bir "token" ile response döner (henüz bunların hiçbirini implement etmedik).
    • "Token", daha sonra bu kullanıcıyı doğrulamak için kullanabileceğimiz içerik taşıyan bir string’dir.
    • Normalde token’ın bir süre sonra süresi dolacak şekilde ayarlanması beklenir.
      • Böylece kullanıcının bir noktada tekrar giriş yapması gerekir.
      • Ayrıca token çalınırsa risk daha düşük olur. Çoğu durumda, sonsuza kadar çalışacak kalıcı bir anahtar gibi değildir.
  • Frontend bu token’ı geçici olarak bir yerde saklar.
  • Kullanıcı frontend’de tıklayarak web uygulamasının başka bir bölümüne gider.
  • Frontend’in API’den daha fazla veri alması gerekir.
    • Ancak o endpoint için authentication gereklidir.
    • Bu yüzden API’mizle authenticate olmak için Authorization header’ını, Bearer + token değeriyle gönderir.
    • Token foobar içeriyorsa Authorization header’ının içeriği Bearer foobar olur.

FastAPI’nin OAuth2PasswordBearer’ı

FastAPI, bu güvenlik özelliklerini implement etmek için farklı soyutlama seviyelerinde çeşitli araçlar sağlar.

Bu örnekte OAuth2’yi, Password flow ile, Bearer token kullanarak uygulayacağız. Bunu OAuth2PasswordBearer sınıfı ile yaparız.

Bilgi

"Bearer" token tek seçenek değildir.

Ama bizim kullanım senaryomuz için en iyi seçenek odur.

Ayrıca bir OAuth2 uzmanı değilseniz ve ihtiyaçlarınıza daha uygun başka bir seçeneğin neden gerekli olduğunu net olarak bilmiyorsanız, çoğu kullanım senaryosu için de en uygun seçenek olacaktır.

Bu durumda bile FastAPI, onu oluşturabilmeniz için gereken araçları sunar.

OAuth2PasswordBearer sınıfının bir instance’ını oluştururken tokenUrl parametresini veririz. Bu parametre, client’ın (kullanıcının browser’ında çalışan frontend’in) token almak için username ve password göndereceği URL’yi içerir.

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}
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

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}

İpucu

Burada tokenUrl="token", henüz oluşturmadığımız göreli bir URL olan token’ı ifade eder. Göreli URL olduğu için ./token ile eşdeğerdir.

Göreli URL kullandığımız için, API’niz https://example.com/ adresinde olsaydı https://example.com/token anlamına gelirdi. Ama API’niz https://example.com/api/v1/ adresinde olsaydı, bu kez https://example.com/api/v1/token anlamına gelirdi.

Göreli URL kullanmak, Behind a Proxy gibi daha ileri kullanım senaryolarında bile uygulamanızın çalışmaya devam etmesini garanti etmek açısından önemlidir.

Bu parametre o endpoint’i / path operation’ı oluşturmaz; fakat /token URL’sinin client’ın token almak için kullanması gereken URL olduğunu bildirir. Bu bilgi OpenAPI’de, dolayısıyla etkileşimli API dokümantasyon sistemlerinde kullanılır.

Birazdan gerçek path operation’ı da oluşturacağız.

Teknik Detaylar

Eğer çok katı bir "Pythonista" iseniz, token_url yerine tokenUrl şeklindeki parametre adlandırma stilini sevmeyebilirsiniz.

Bunun nedeni, OpenAPI spesifikasyonundaki isimle aynı adın kullanılmasıdır. Böylece bu güvenlik şemalarından herhangi biri hakkında daha fazla araştırma yapmanız gerekirse, adı kopyalayıp yapıştırarak kolayca daha fazla bilgi bulabilirsiniz.

oauth2_scheme değişkeni, OAuth2PasswordBearer’ın bir instance’ıdır; ama aynı zamanda "callable"dır.

Şu şekilde çağrılabilir:

oauth2_scheme(some, parameters)

Dolayısıyla Depends ile kullanılabilir.

Kullanın

Artık Depends ile bir dependency olarak oauth2_scheme’i geçebilirsiniz.

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}
🤓 Other versions and variants

Tip

Prefer to use the Annotated version if possible.

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}

Bu dependency, path operation function içindeki token parametresine atanacak bir str sağlar.

FastAPI, bu dependency’yi OpenAPI şemasında (ve otomatik API dokümanlarında) bir "security scheme" tanımlamak için kullanabileceğini bilir.

Teknik Detaylar

FastAPI, bir dependency içinde tanımlanan OAuth2PasswordBearer sınıfını OpenAPI’de security scheme tanımlamak için kullanabileceğini bilir; çünkü bu sınıf fastapi.security.oauth2.OAuth2’den kalıtım alır, o da fastapi.security.base.SecurityBase’den kalıtım alır.

OpenAPI (ve otomatik API dokümanları) ile entegre olan tüm security araçları SecurityBase’den kalıtım alır; FastAPI bu sayede onları OpenAPI’ye nasıl entegre edeceğini anlayabilir.

Ne Yapar

Request içinde Authorization header’ını arar, değerin Bearer + bir token olup olmadığını kontrol eder ve token’ı str olarak döndürür.

Eğer Authorization header’ını görmezse ya da değer Bearer token’ı içermiyorsa, doğrudan 401 status code hatasıyla (UNAUTHORIZED) response döner.

Token’ın var olup olmadığını kontrol edip ayrıca hata döndürmenize bile gerek yoktur. Fonksiyonunuz çalışıyorsa, token içinde bir str olacağından emin olabilirsiniz.

Bunu şimdiden etkileşimli dokümanlarda deneyebilirsiniz:

Henüz token’ın geçerliliğini doğrulamıyoruz, ama başlangıç için bu bile yeterli.

Özet

Yani sadece 3 veya 4 ekstra satırla, şimdiden ilkel de olsa bir güvenlik katmanı elde etmiş oldunuz.