IlmHamroh
Python kursi/Web backend FastAPI10/16-dars17 daqiqa
Mundarija (22)

20.10-dars: Autentifikatsiya — JWT

20-QISM — WEB-BACKEND (FastAPI) · 10-dars


1. Kirish va motivatsiya

CRUD API tayyor, lekin hamma hamma narsani qila oladi — istalgan odam kurs o'chira oladi. Real API foydalanuvchini tanishi (autentifikatsiya) kerak: kim so'rov yuboryapti? U tizimga kirganmi? Bu darsda autentifikatsiya — foydalanuvchini tanishni o'rganamiz.

Zamonaviy API'lar JWT (JSON Web Token) ishlatadi. Foydalanuvchi login/parol bilan bir marta kiradi, server unga token (imzolangan ma'lumot) beradi. Keyingi har so'rovda foydalanuvchi tokenni yuboradi — server uni tekshiradi (bazaga qaramasdan). Token — "kirish chiptasi": bir marta olinadi, ko'p marta ishlatiladi.

Real vaziyat. Bir API har so'rovda login/parolni so'rardi — mijoz ilovasi parolni saqlab, har so'rovga qo'shishga majbur edi (xavfli). Va har so'rovda parol bazada tekshirilardi (sekin, parol hash'i har safar hisoblanardi). Jamoa JWT'ga o'tdi: bir marta login → token, keyingi so'rovlar token bilan. Parol faqat bir marta uzatildi, token bazasiz tekshirildi (imzo orqali). Xavfsizlik va tezlik oshdi.

Bu darsda JWT autentifikatsiyani — parol hash, token yaratish/tekshirish va himoyalashni o'rganamiz.

Bu darsda:

  • Parol hash (bcrypt — parolni hech qachon ochiq saqlamang)
  • JWT token (yaratish, tekshirish)
  • Login endpoint (OAuth2PasswordRequestForm)
  • OAuth2PasswordBearer bilan token olish
  • joriy_foydalanuvchi bog'liqligi
  • Token muddati (exp)
  • Xato holatlar (401)
  • Amaliy: to'liq auth tizimi

ℹ Misollarda FastAPI TestClient, PyJWT va bcrypt bilan sinaladi.


2. Nazariya — chuqur tushuntirish

2.1. Parolni hech qachon ochiq saqlamang

Parollar bazada hash ko'rinishida saqlanadi (hech qachon ochiq matn):

python
import bcrypt

def hash_parol(parol: str) -> str:
    return bcrypt.hashpw(parol.encode(), bcrypt.gensalt()).decode()

def tekshir_parol(parol: str, hash_: str) -> bool:
    return bcrypt.checkpw(parol.encode(), hash_.encode())
Amal Natija
hashpw Parol → hash ($2b$12$...)
checkpw Parol hash'ga mos keladimi
gensalt Tasodifiy "tuz" (salt)

Hash — bir tomonlama: paroldan hash olinadi, lekin hash'dan parol tiklanmaydi. bcrypt sekin (ataylab) — brute-force'ni qiyinlashtiradi. Baza o'g'irlansa ham parollar xavfsiz.

2.2. JWT nima

JWT — imzolangan, o'z ichida ma'lumot saqlovchi token. Uch qismdan iborat (nuqta bilan):

xxxxx.yyyyy.zzzzz
 │      │      └── imzo (signature)
 │      └── ma'lumot (payload: sub, exp, ...)
 └── sarlavha (header: algoritm)
Qism Ma'no
Header Algoritm (HS256)
Payload Ma'lumot (sub — kim, exp — muddat)
Signature Imzo (maxfiy kalit bilan)

JWT — o'zini tasdiqlovchi: imzo maxfiy kalit bilan yaratiladi. Kim payload'ni o'zgartirsa, imzo mos kelmaydi. Server bazaga qaramasdan tokenni tekshiradi.

2.3. Token yaratish va tekshirish

python
import jwt      # PyJWT
import datetime

SIR = "maxfiy-kalit"        # .env'da saqlanadi!
ALGO = "HS256"

def token_yarat(ism: str) -> str:
    payload = {
        "sub": ism,
        "exp": datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(minutes=30),
    }
    return jwt.encode(payload, SIR, algorithm=ALGO)

def token_oqi(token: str) -> dict:
    return jwt.decode(token, SIR, algorithms=[ALGO])   # imzo + muddat tekshiriladi

jwt.encode — payload'ni imzolab token yaratadi; jwt.decode — imzo va muddatni tekshiradi (xato bo'lsa PyJWTError). Maxfiy kalit — .envda (kodda emas!).

2.4. Login endpoint

Foydalanuvchi login/parol bilan token oladi:

python
from fastapi.security import OAuth2PasswordRequestForm

@app.post("/token")
def kirish(form: Annotated[OAuth2PasswordRequestForm, Depends()]):
    user = FOYDALANUVCHILAR.get(form.username)
    if not user or not tekshir_parol(form.password, user["parol_hash"]):
        raise HTTPException(status_code=401, detail="login yoki parol xato")
    return {"access_token": token_yarat(user["ism"]), "token_type": "bearer"}

OAuth2PasswordRequestForm — standart login formasi (username, password). Parol to'g'ri bo'lsa token qaytaradi; xato bo'lsa 401. Javob {"access_token": ..., "token_type": "bearer"}.

2.5. OAuth2PasswordBearer bilan token olish

Himoyalangan endpoint tokenni Authorization: Bearer <token> sarlavhasidan oladi:

python
from fastapi.security import OAuth2PasswordBearer

oauth2 = OAuth2PasswordBearer(tokenUrl="token")

@app.get("/men")
def men(token: Annotated[str, Depends(oauth2)]):
    ...    # token — sarlavhadan olindi

OAuth2PasswordBearer — Authorization: Bearer <token> sarlavhasidan tokenni oladi. Token yo'q bo'lsa avtomatik 401. tokenUrl — login endpoint (hujjat uchun).

2.6. joriy_foydalanuvchi bog'liqligi

Tokenni foydalanuvchiga aylantiruvchi bog'liqlik 20.7-bob:

python
def joriy_foydalanuvchi(token: Annotated[str, Depends(oauth2)]):
    try:
        payload = token_oqi(token)
    except jwt.PyJWTError:
        raise HTTPException(status_code=401, detail="token yaroqsiz")
    return FOYDALANUVCHILAR[payload["sub"]]

@app.get("/men")
def men(user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
    return {"ism": user["ism"]}

joriy_foydalanuvchi — token o'qib, foydalanuvchini qaytaradi (yoki 401). Har himoyalangan endpoint uni Depends bilan ishlatadi — auth mantigi bir joyda.

2.7. Token muddati (exp)

Token abadiy emas — exp (expiration) bilan muddat beriladi:

python
"exp": datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(minutes=30)
Holat Natija
Muddat ichida Qabul qilinadi
Muddat o'tgan ExpiredSignatureError → 401

exp — token qachon eskirishini belgilaydi. jwt.decode muddat o'tgan bo'lsa ExpiredSignatureError beradi. Qisqa muddat (30 daqiqa) — o'g'irlangan token ko'p zarar bermaydi.

2.8. Xato holatlar

Auth turli xatolarni 401 bilan boshqaradi:

Xato Sabab
Parol xato Login noto'g'ri
Token yo'q Sarlavha berilmagan
Token buzuq InvalidTokenError
Imzo noto'g'ri InvalidSignatureError
Muddat o'tgan ExpiredSignatureError

Barcha auth xatolari 401 Unauthorized. jwt.PyJWTError — barcha JWT xatolarining asosiy klassi (ularni birga ushlash). Xato xabari umumiy bo'lsin ("token yaroqsiz") — hujumchiga ma'lumot bermang.


3. Tez ma'lumotnoma

python
import jwt, bcrypt, datetime
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm

# parol:
bcrypt.hashpw(parol.encode(), bcrypt.gensalt()).decode()   # hash
bcrypt.checkpw(parol.encode(), hash_.encode())             # tekshir

# token:
jwt.encode({"sub": ism, "exp": ...}, SIR, algorithm="HS256")   # yaratish
jwt.decode(token, SIR, algorithms=["HS256"])                   # o'qish

# login:
oauth2 = OAuth2PasswordBearer(tokenUrl="token")
@app.post("/token")
def kirish(form: Annotated[OAuth2PasswordRequestForm, Depends()]): ...

# himoya:
def joriy_foydalanuvchi(token: Annotated[str, Depends(oauth2)]): ...

Auth oqimi

1. login/parol → POST /token → token
2. token'ni saqlash (mijoz)
3. har so'rovda: Authorization: Bearer <token>
4. server: token tekshir → foydalanuvchi

4. Batafsil misollar

Misollarda FastAPI TestClient, PyJWT va bcrypt bilan sinaladi.

Misol 1 — Parol hash va token

python
"""bcrypt bilan parol hash va tekshirish (bir tomonlama); JWT token yaratish va o'qish; token 3 qismli; payload."""

import warnings
warnings.filterwarnings("ignore")

import datetime

import bcrypt
import jwt

SIR = "namuna-maxfiy-kalit"
ALGO = "HS256"


def hash_parol(parol: str) -> str:
    return bcrypt.hashpw(parol.encode(), bcrypt.gensalt()).decode()


def tekshir_parol(parol: str, hash_: str) -> bool:
    return bcrypt.checkpw(parol.encode(), hash_.encode())


def token_yarat(ism: str) -> str:
    payload = {
        "sub": ism,
        "exp": datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(minutes=30),
    }
    return jwt.encode(payload, SIR, algorithm=ALGO)


def main() -> None:
    print("=== 1. Parol hash (bir tomonlama) ===")
    h = hash_parol("sirli123")
    print(f"  hash boshi: {h[:7]} (bcrypt belgisi)")
    print(f"  hash uzunligi: {len(h)}")

    print("\n=== 2. Parol tekshirish ===")
    print(f"  to'g'ri parol: {tekshir_parol('sirli123', h)}")
    print(f"  xato parol: {tekshir_parol('boshqa', h)}")

    print("\n=== 3. Token yaratish ===")
    token = token_yarat("ali")
    print(f"  token qismlari (nuqta bilan): {len(token.split('.'))}")

    print("\n=== 4. Token o'qish (payload) ===")
    payload = jwt.decode(token, SIR, algorithms=[ALGO])
    print(f"  sub (kim): {payload['sub']}")
    print(f"  exp bor: {'exp' in payload}")
    print("  ⭐ parol — hash (bir tomonlama); token — imzolangan ma'lumot")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Parol hash (bir tomonlama) ===
  hash boshi: $2b$12$ (bcrypt belgisi)
  hash uzunligi: 60

=== 2. Parol tekshirish ===
  to'g'ri parol: True
  xato parol: False

=== 3. Token yaratish ===
  token qismlari (nuqta bilan): 3

=== 4. Token o'qish (payload) ===
  sub (kim): ali
  exp bor: True
  ⭐ parol — hash (bir tomonlama); token — imzolangan ma'lumot

Nima ko'rsatdi: 2.1, 2.2, 2.3-bo'limlar.

Misol 2 — Token xato holatlari

python
"""imzo tekshiruvi (noto'g'ri kalit → InvalidSignatureError); muddat (o'tgan → ExpiredSignatureError); buzuq token; PyJWTError."""

import warnings
warnings.filterwarnings("ignore")

import datetime

import jwt

SIR = "to'g'ri-kalit"
ALGO = "HS256"


def main() -> None:
    print("=== 1. To'g'ri token o'qiladi ===")
    token = jwt.encode({"sub": "ali"}, SIR, algorithm=ALGO)
    print(f"  decode → sub={jwt.decode(token, SIR, algorithms=[ALGO])['sub']}")

    print("\n=== 2. ⚠️ Noto'g'ri imzo (boshqa kalit) ===")
    try:
        jwt.decode(token, "yolgon-kalit", algorithms=[ALGO])
    except jwt.InvalidSignatureError:
        print("  InvalidSignatureError — imzo mos kelmadi")

    print("\n=== 3. ⚠️ Muddati o'tgan token ===")
    eski = jwt.encode(
        {"sub": "ali", "exp": datetime.datetime(2000, 1, 1, tzinfo=datetime.timezone.utc)},
        SIR, algorithm=ALGO,
    )
    try:
        jwt.decode(eski, SIR, algorithms=[ALGO])
    except jwt.ExpiredSignatureError:
        print("  ExpiredSignatureError — token eskirgan")

    print("\n=== 4. ⚠️ Buzuq token ===")
    try:
        jwt.decode("bu.buzuq.token", SIR, algorithms=[ALGO])
    except jwt.PyJWTError as e:
        print(f"  PyJWTError — {type(e).__name__} (barcha JWT xatolari asosi)")
    print("  ⭐ imzo/muddat/format tekshiriladi; PyJWTError — umumiy asos")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. To'g'ri token o'qiladi ===
  decode → sub=ali

=== 2. ⚠️ Noto'g'ri imzo (boshqa kalit) ===
  InvalidSignatureError — imzo mos kelmadi

=== 3. ⚠️ Muddati o'tgan token ===
  ExpiredSignatureError — token eskirgan

=== 4. ⚠️ Buzuq token ===
  PyJWTError — DecodeError (barcha JWT xatolari asosi)
  ⭐ imzo/muddat/format tekshiriladi; PyJWTError — umumiy asos

Nima ko'rsatdi: 2.2, 2.7, 2.8-bo'limlar.

Misol 3 — Login va himoyalangan endpoint

python
"""login endpoint (OAuth2PasswordRequestForm); token qaytarish; OAuth2PasswordBearer; joriy_foydalanuvchi bog'liqligi; 401."""

import warnings
warnings.filterwarnings("ignore")

import datetime
from typing import Annotated

import bcrypt
import jwt
from fastapi import Depends, FastAPI, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from fastapi.testclient import TestClient

SIR = "namuna-kalit"
ALGO = "HS256"

app = FastAPI()
oauth2 = OAuth2PasswordBearer(tokenUrl="token")


def _hash(p: str) -> str:
    return bcrypt.hashpw(p.encode(), bcrypt.gensalt()).decode()


FOYDALANUVCHILAR = {
    "ali": {"ism": "ali", "parol_hash": _hash("sirli123"), "rol": "admin"},
}


@app.post("/token")
def kirish(form: Annotated[OAuth2PasswordRequestForm, Depends()]):
    user = FOYDALANUVCHILAR.get(form.username)
    if not user or not bcrypt.checkpw(form.password.encode(), user["parol_hash"].encode()):
        raise HTTPException(status_code=401, detail="login yoki parol xato")
    payload = {
        "sub": user["ism"],
        "exp": datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(minutes=30),
    }
    return {"access_token": jwt.encode(payload, SIR, algorithm=ALGO), "token_type": "bearer"}


def joriy_foydalanuvchi(token: Annotated[str, Depends(oauth2)]):
    try:
        payload = jwt.decode(token, SIR, algorithms=[ALGO])
    except jwt.PyJWTError:
        raise HTTPException(status_code=401, detail="token yaroqsiz")
    return FOYDALANUVCHILAR[payload["sub"]]


@app.get("/men")
def men(user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
    return {"ism": user["ism"], "rol": user["rol"]}


def main() -> None:
    client = TestClient(app)

    print("=== 1. Login (to'g'ri) ===")
    javob = client.post("/token", data={"username": "ali", "password": "sirli123"})
    print(f"  status {javob.status_code}, token_type: {javob.json()['token_type']}")
    token = javob.json()["access_token"]

    print("\n=== 2. Himoyalangan endpoint (token bilan) ===")
    javob = client.get("/men", headers={"Authorization": f"Bearer {token}"})
    print(f"  /men → status {javob.status_code}, {javob.json()}")

    print("\n=== 3. ⚠️ Tokensiz ===")
    print(f"  /men (tokensiz) → status {client.get('/men').status_code}")

    print("\n=== 4. ⚠️ Xato login ===")
    javob = client.post("/token", data={"username": "ali", "password": "xato"})
    print(f"  xato parol → status {javob.status_code}, {javob.json()['detail']}")
    print("  ⭐ login → token; har so'rov: Authorization: Bearer <token>")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Login (to'g'ri) ===
  status 200, token_type: bearer

=== 2. Himoyalangan endpoint (token bilan) ===
  /men → status 200, {'ism': 'ali', 'rol': 'admin'}

=== 3. ⚠️ Tokensiz ===
  /men (tokensiz) → status 401

=== 4. ⚠️ Xato login ===
  xato parol → status 401, login yoki parol xato
  ⭐ login → token; har so'rov: Authorization: Bearer <token>

Nima ko'rsatdi: 2.4, 2.5, 2.6-bo'limlar.

Misol 4 — Amaliy: to'liq auth tizimi

Ro'yxatdan o'tish (register), kirish (login) va himoyalangan resurs — to'liq auth tizimini yaratamiz. Yangi foydalanuvchi ro'yxatdan o'tadi (parol hash bilan saqlanadi), kiradi (token oladi), va himoyalangan endpointga kiradi.

python
"""to'liq auth: register (parol hash bilan saqlash); login (token); himoyalangan endpoint; token bilan/siz; buzuq token → 401."""

import warnings
warnings.filterwarnings("ignore")

import datetime
from typing import Annotated

import bcrypt
import jwt
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from fastapi.testclient import TestClient
from pydantic import BaseModel

SIR = "namuna-maxfiy-kalit"
ALGO = "HS256"

app = FastAPI(title="Wisar Auth API")
oauth2 = OAuth2PasswordBearer(tokenUrl="token")

BAZA: dict[str, dict] = {}


class RoyxatKirish(BaseModel):
    ism: str
    parol: str


def token_yarat(ism: str) -> str:
    payload = {
        "sub": ism,
        "exp": datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(minutes=30),
    }
    return jwt.encode(payload, SIR, algorithm=ALGO)


@app.post("/register", status_code=201)
def royxat(kirish: RoyxatKirish):
    if kirish.ism in BAZA:
        raise HTTPException(status_code=400, detail="bu ism band")
    BAZA[kirish.ism] = {
        "ism": kirish.ism,
        "parol_hash": bcrypt.hashpw(kirish.parol.encode(), bcrypt.gensalt()).decode(),
    }
    return {"ism": kirish.ism, "holat": "ro'yxatdan o'tdi"}


@app.post("/token")
def kirish(form: Annotated[OAuth2PasswordRequestForm, Depends()]):
    user = BAZA.get(form.username)
    if not user or not bcrypt.checkpw(form.password.encode(), user["parol_hash"].encode()):
        raise HTTPException(status_code=401, detail="login yoki parol xato")
    return {"access_token": token_yarat(user["ism"]), "token_type": "bearer"}


def joriy_foydalanuvchi(token: Annotated[str, Depends(oauth2)]):
    xato = HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="token yaroqsiz")
    try:
        payload = jwt.decode(token, SIR, algorithms=[ALGO])
    except jwt.PyJWTError:
        raise xato
    user = BAZA.get(payload.get("sub"))
    if user is None:
        raise xato
    return user


@app.get("/profil")
def profil(user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
    return {"ism": user["ism"]}


def main() -> None:
    client = TestClient(app)

    print("=== 1. Ro'yxatdan o'tish ===")
    javob = client.post("/register", json={"ism": "vali", "parol": "parol123"})
    print(f"  status {javob.status_code} → {javob.json()}")

    print("\n=== 2. Kirish (login) ===")
    javob = client.post("/token", data={"username": "vali", "password": "parol123"})
    print(f"  status {javob.status_code}, token olindi: {'access_token' in javob.json()}")
    token = javob.json()["access_token"]

    print("\n=== 3. Himoyalangan profil ===")
    javob = client.get("/profil", headers={"Authorization": f"Bearer {token}"})
    print(f"  /profil → status {javob.status_code}, {javob.json()}")

    print("\n=== 4. ⚠️ Xato holatlar ===")
    print(f"  band ism → status {client.post('/register', json={'ism': 'vali', 'parol': 'x'}).status_code}")
    print(f"  buzuq token → status {client.get('/profil', headers={'Authorization': 'Bearer buzuq'}).status_code}")
    print("  ⭐ register (hash) → login (token) → himoyalangan resurs")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ro'yxatdan o'tish ===
  status 201 → {'ism': 'vali', 'holat': "ro'yxatdan o'tdi"}

=== 2. Kirish (login) ===
  status 200, token olindi: True

=== 3. Himoyalangan profil ===
  /profil → status 200, {'ism': 'vali'}

=== 4. ⚠️ Xato holatlar ===
  band ism → status 400
  buzuq token → status 401
  ⭐ register (hash) → login (token) → himoyalangan resurs

Nima ko'rsatdi: 2.1–2.8-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Parolni ochiq saqlash" Har doim hash (bcrypt)
"Hash'dan parol tiklanadi" Bir tomonlama
"Token — maxfiy" Token o'qiladi (imzo himoya)
"JWT'da parol bo'lsin" Hech qachon — faqat sub, exp
"Token abadiy" exp — muddat bor
"Maxfiy kalit kodda" .envda (git'ga emas)
"Har so'rovda login" Bir marta login → token
"Token bazada tekshiriladi" Imzo orqali (bazasiz)

6. Keng tarqalgan xatolar va yechimlari

1. Parolni ochiq saqlash

python
BAZA[ism] = {"parol": parol}                # ❌ XAVFLI
BAZA[ism] = {"parol_hash": hash_parol(parol)}   # ✅

2. Maxfiy kalitni kodda

python
SIR = "12345"                               # ❌ kodda/git'da
SIR = os.environ["JWT_SIR"]                 # ✅ .env'dan

3. exp bermaslik (abadiy token)

python
jwt.encode({"sub": ism}, SIR, ...)          # ⚠️ muddatsiz
jwt.encode({"sub": ism, "exp": ...}, ...)   # ✅

4. JWT'da maxfiy ma'lumot

python
{"sub": ism, "parol": parol}                # ❌ payload o'qiladi!
{"sub": ism, "exp": ...}                     # ✅

5. Xato xabari ko'p ma'lumot berish

python
raise HTTPException(401, "parol xato")       # ⚠️ login bor deydi
raise HTTPException(401, "login yoki parol xato")   # ✅ umumiy

6. Bitta xato turini ushlash

python
except jwt.ExpiredSignatureError:           # ⚠️ boshqalari o'tib ketadi
except jwt.PyJWTError:                        # ✅ barcha JWT xatolari

7. algorithms ro'yxatsiz decode

python
jwt.decode(token, SIR)                       # ❌ algoritm kerak
jwt.decode(token, SIR, algorithms=["HS256"]) # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 20.6-dars (o'tilgan): sarlavhalar — Authorization: Bearer
  • 20.7-dars (o'tilgan): bog'liqlik — joriy_foydalanuvchi
  • 20.11-dars: ruxsatlar — kim nima qila oladi (rol)
  • 20.12-dars: xatolar — 401/403 boshqaruvi
  • 16.4-dars (o'tilgan): xavfsizlik — hash, tuz tushunchasi

8. Eng yaxshi amaliyotlar

  1. Parolni har doim hash qiling (bcrypt) — hech qachon ochiq.

  2. Maxfiy kalitni .envda saqlang (git'ga emas).

  3. Token payload'iga maxfiy ma'lumot qo'ymang (o'qiladi).

  4. exp bilan token muddatini cheklang (30 daqiqa).

  5. Barcha JWT xatolarini jwt.PyJWTError bilan ushlang.

  6. Auth mantiqini bog'liqlik qiling (joriy_foydalanuvchi).

  7. Xato xabari umumiy bo'lsin (hujumchiga ma'lumot bermang).

  8. decodeda algorithms ni aniq bering (xavfsizlik).


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # parol qanday saqlanadi?
2.  # hash bir tomonlamami?
3.  # JWT necha qismdan iborat?
4.  # JWT payload maxfiymi?
5.  # exp nima?
6.  # maxfiy kalit qayerda?
7.  # login qaysi status (xato parol)?
8.  # tokensiz himoyalangan endpoint?
9.  # OAuth2PasswordBearer nima oladi?
10. # PyJWTError nima?
11. # token bazada tekshiriladimi?
12. # payload'ga parol qo'ysa bo'ladimi?
Javoblar
  1. Hash (bcrypt) — ochiq emas
  2. Ha (hash'dan parol tiklanmaydi)
  3. Uch (header.payload.signature)
  4. Yo'q (o'qiladi; imzo himoya qiladi)
  5. Token muddati (expiration)
  6. .envda (kod/git'da emas)
  7. 401
  8. 401
  9. Authorization: Bearer <token> sarlavhasidan tokenni
  10. Barcha JWT xatolarining asosiy klassi
  11. Yo'q (imzo orqali, bazasiz)
  12. Yo'q (payload o'qiladi — maxfiy emas)

Vazifa 2: Xatolarni tuzating

python
1.  BAZA[ism] = {"parol": parol}        # saqlash

2.  SIR = "12345"                        # kalit

3.  jwt.encode({"sub": ism}, SIR, ...)   # muddat

4.  {"sub": ism, "parol": parol}         # payload

5.  except jwt.ExpiredSignatureError:    # xato turi
Javoblar
python
1.  BAZA[ism] = {"parol_hash": hash_parol(parol)}

2.  SIR = os.environ["JWT_SIR"]

3.  jwt.encode({"sub": ism, "exp": ...}, SIR, ...)

4.  {"sub": ism, "exp": ...}             # maxfiy ma'lumotsiz

5.  except jwt.PyJWTError:               # barcha JWT xatolari

Vazifa 3: Register va login

To'liq auth oqimi:

  1. POST /register (parol hash bilan)
  2. POST /token (login → token)
  3. GET /profil (himoyalangan)
  4. TestClient bilan to'liq oqim test

Vazifa 4: Token muddati

Muddat bilan ishlash:

  1. Qisqa muddatli token (test uchun o'tgan)
  2. ExpiredSignatureError ushlash → 401
  3. Yangi token olish (refresh tushunchasi)
  4. Muddat holatlarini test

Vazifa 5: Parol xavfsizligi

Parol qoidalari:

  1. field_validator bilan parol kuchi (uzunlik, raqam, katta harf)
  2. Ro'yxatda tekshirish
  3. Hash saqlash
  4. Zaif parol → 422 test

Vazifa 6: Xato holatlari to'plami

Barcha auth xatolarini test:

  1. Xato parol, band ism
  2. Tokensiz, buzuq token
  3. Muddati o'tgan token
  4. Noto'g'ri imzo
  5. Har biri to'g'ri status/xabar

Vazifa 7: O'ylash

JWT autentifikatsiya holatsiz (stateless): server tokenni imzo orqali tekshiradi, sessiya ma'lumotini saqlamaydi. Bu an'anaviy sessiya (server sessiyani xotirada/bazada saqlaydi) dan farq qiladi. Holatsiz autentifikatsiyaning afzalligi (masshtablanish) va kamchiligi (tokenni bekor qilish qiyin) nima, va bu "holat qayerda saqlanadi" tanlovi tizim dizaynida qanchalik muhim?

Javob

Qisqa javob: JWT holatsiz — sessiya ma'lumoti tokenda (mijozda), serverda emas. Server tokenni imzo orqali tekshiradi, hech narsa saqlamaydi. Afzalligi: masshtablanish — har server tokenni mustaqil tekshiradi (umumiy sessiya xotirasi kerak emas), ko'p server oson qo'shiladi. Kamchiligi: tokenni bekor qilish qiyin — token muddati tugaguncha amal qiladi (server "unut" deb ayta olmaydi, chunki hech narsa saqlamaydi). Bu "holat qayerda" tanlovi tizim dizaynining markaziy savoli.

1. Holatsiz vs holatli

Holatli (sessiya) Holatsiz (JWT)
Server sessiyani saqlaydi Token o'zida ma'lumot
Bekor qilish oson Bekor qilish qiyin
Umumiy xotira kerak Har server mustaqil
Masshtablanish qiyin Masshtablanish oson

2. Holatsizlik afzalligi

Afzallik Sabab
Masshtablanish Umumiy sessiya yo'q
Tezlik Bazasiz tekshiruv
Oddiy server Holat saqlamaydi
Mikroservis Har xizmat mustaqil tekshiradi

3. Holatsizlik kamchiligi

Kamchilik Sabab
Bekor qilish qiyin Server "unut" ayta olmaydi
Yangilash murakkab Refresh token kerak
Token o'g'irlansa Muddat tugaguncha amal

Yechim: qisqa muddat + refresh token; yoki qora ro'yxat (blacklist — holat qo'shadi).

4. "Holat qayerda" — markaziy tanlov

Holat joyi Ta'sir
Serverda (sessiya) Nazorat, lekin masshtablanish qiyin
Mijozda (JWT) Masshtablanish, lekin nazorat kam
Bazada Ishonchli, lekin sekin

5. Muhandislik saboqlari

  1. "Holat qayerda" — dizaynning asosiy savoli
  2. Holatsiz — masshtablanish, lekin bekor qilish qiyin
  3. Har tanlovda o'zaro kelishuv (trade-off)
  4. Kombinatsiya (JWT + refresh) — ko'p holda yechim

6. Xulosa

  1. JWT holatsiz — sessiya tokenda, serverda emas
  2. Afzallik: masshtablanish; kamchilik: bekor qilish
  3. Refresh token/qisqa muddat — kamchilikni yumshatadi
  4. "Holat qayerda" — tizim dizaynining markaziy tanlovi

Nimani mustahkamlaydi: 2.1–2.8-bo'limlar.


Xulosa

Bu darsda JWT autentifikatsiyani — foydalanuvchini tanishni o'rgandik.

Eng muhim uch fikr:

  1. Parol hash — xavfsizlik asosi. Parollar bazada hech qachon ochiq saqlanmaydi — bcrypt bilan hash qilinadi. Hash bir tomonlama: paroldan hash olinadi, lekin hash'dan parol tiklanmaydi. bcrypt.hashpw (yaratish) va bcrypt.checkpw (tekshirish) — parolni xavfsiz boshqaradi. Baza o'g'irlansa ham parollar himoyalangan.

  2. JWT — holatsiz token. JWT uch qismdan iborat (header.payload.signature), imzo maxfiy kalit bilan yaratiladi. jwt.encode tokenni imzolaydi, jwt.decode imzo va muddat (exp) ni tekshiradi. Token o'qiladi (maxfiy emas) — shuning uchun payload'iga parol yoki maxfiy ma'lumot qo'yilmaydi, faqat sub (kim) va exp (muddat). Maxfiy kalit .envda saqlanadi (kodda/git'da emas).

  3. Login oqimi va himoya. Foydalanuvchi OAuth2PasswordRequestForm bilan login/parol yuboradi, to'g'ri bo'lsa token oladi. Keyingi so'rovlar Authorization: Bearer <token> sarlavhasini yuboradi; OAuth2PasswordBearer tokenni oladi, joriy_foydalanuvchi bog'liqligi uni foydalanuvchiga aylantiradi (yoki 401). Barcha auth xatolari (xato parol, buzuq/muddati o'tgan token, noto'g'ri imzo) 401 bilan boshqariladi — jwt.PyJWTError barchasini birga ushlaydi.

Keyingi darsda ruxsatlar ni o'rganamiz: autentifikatsiya "kim" ni aniqladi, ruxsat esa "u nima qila oladi" ni belgilaydi — rollar (admin, oddiy), egalik tekshiruvi va Depends bilan ruxsat qatlamlari.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
20.10-dars: Autentifikatsiya — JWT — IlmHamroh