← Назад к вопросам

Какие методы аутентификации используются в абстрактных веб-сервисах?

2.0 Middle🔥 191 комментариев
#Безопасность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Методы аутентификации в веб-сервисах

Аутентификация - это процесс проверки личности пользователя. Разберём основные методы, используемые в современных веб-приложениях.

1. HTTP Basic Authentication

Наиболее простой метод: передача логина и пароля в Base64:

import base64
import secrets
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials

security = HTTPBasic()

def verify_basic_auth(credentials: HTTPBasicCredentials = Depends(security)):
    """Проверка Basic Auth"""
    correct_username = secrets.compare_digest(credentials.username, "admin")
    correct_password = secrets.compare_digest(credentials.password, "secret")
    
    if not (correct_username and correct_password):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid credentials",
            headers={"WWW-Authenticate": "Basic"},
        )
    return credentials.username

# На клиенте
def send_basic_auth_request():
    username = "admin"
    password = "secret"
    credentials = base64.b64encode(f"{username}:{password}".encode()).decode()
    headers = {"Authorization": f"Basic {credentials}"}
    # Отправить с headers

Плюсы: Простота Минусы: Небезопасно без HTTPS, логин/пароль в каждом запросе

2. API Key Authentication

Передача API ключа в заголовке или параметре:

from fastapi import Depends, HTTPException, status
from fastapi.security import APIKeyHeader, APIKeyCookie, APIKeyQuery

# Вариант 1: APIKeyHeader
api_key_header = APIKeyHeader(name="X-API-Key")

def verify_api_key(api_key: str = Depends(api_key_header)):
    if api_key != "secret-api-key-12345":
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Invalid API key"
        )
    return api_key

# Вариант 2: APIKeyCookie
api_key_cookie = APIKeyCookie(name="api_key")

def verify_cookie_api_key(api_key: str = Depends(api_key_cookie)):
    if api_key != "valid-api-key":
        raise HTTPException(status_code=403, detail="Invalid API key")
    return api_key

# Вариант 3: APIKeyQuery
api_key_query = APIKeyQuery(name="api_key")

@app.get("/protected")
async def protected_endpoint(api_key: str = Depends(api_key_query)):
    return {"message": f"Hello, your API key is: {api_key}"}

# На клиенте
import requests

def call_api():
    headers = {"X-API-Key": "secret-api-key-12345"}
    response = requests.get("http://api.example.com/data", headers=headers)
    return response.json()

Плюсы: Простая реализация, подходит для сервис-сервис общения Минусы: Ключ видим в логах, нельзя отозвать за сессию

3. Token-Based Authentication (JWT)

Один из самых популярных методов для SPA приложений:

from datetime import datetime, timedelta, timezone
from typing import Optional
import jwt
from passlib.context import CryptContext
from pydantic import BaseModel
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

# Константы
SECRET_KEY = "your-secret-key-change-in-production"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

# Хеширование пароля
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

class Token(BaseModel):
    access_token: str
    token_type: str
    expires_in: int

class TokenData(BaseModel):
    username: Optional[str] = None

class User(BaseModel):
    username: str
    email: str
    full_name: Optional[str] = None

def hash_password(password: str) -> str:
    return pwd_context.hash(password)

def verify_password(plain_password: str, hashed_password: str) -> bool:
    return pwd_context.verify(plain_password, hashed_password)

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
    """Создание JWT токена"""
    to_encode = data.copy()
    
    if expires_delta:
        expire = datetime.now(timezone.utc) + expires_delta
    else:
        expire = datetime.now(timezone.utc) + timedelta(minutes=15)
    
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

security = HTTPBearer()

async def verify_jwt_token(credentials: HTTPAuthorizationCredentials = Depends(security)) -> str:
    """Проверка JWT токена"""
    token = credentials.credentials
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid token"
            )
    except jwt.ExpiredSignatureError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Token expired"
        )
    except jwt.InvalidTokenError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid token"
        )
    return username

# Эндпоинт логина
@app.post("/login", response_model=Token)
async def login(username: str, password: str):
    # Проверка логина/пароля (упрощено)
    if username != "admin" or not verify_password(password, hash_password("secret")):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid credentials"
        )
    
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": username},
        expires_delta=access_token_expires
    )
    
    return {
        "access_token": access_token,
        "token_type": "bearer",
        "expires_in": ACCESS_TOKEN_EXPIRE_MINUTES * 60
    }

@app.get("/protected")
async def protected_route(username: str = Depends(verify_jwt_token)):
    return {"message": f"Hello {username}"}

Плюсы: Безопасный, не требует отправки пароля каждый раз, можно хранить на клиенте Минусы: Невозможно инвалидировать до истечения, нужна синхронизация ключа

4. OAuth 2.0

Стандарт для делегированного доступа (сторонние сервисы):

from authlib.integrations.starlette_client import OAuth
from starlette.applications import Starlette
from starlette.middleware.sessions import SessionMiddleware

app = Starlette()
app.add_middleware(SessionMiddleware, secret_key="your-secret")

oauth = OAuth()
oauth.register(
    name="google",
    client_id="YOUR_CLIENT_ID",
    client_secret="YOUR_CLIENT_SECRET",
    server_metadata_url="https://accounts.google.com/.well-known/openid-configuration",
    client_kwargs={"scope": "openid email profile"}
)

@app.route("/login")
async def login(request):
    redirect_uri = request.url_for("callback")
    return await oauth.google.authorize_redirect(request, redirect_uri)

@app.route("/callback")
async def callback(request):
    token = await oauth.google.authorize_access_token(request)
    user = token.get("userinfo")
    # Сохранить пользователя в БД
    return {"user": user}

Плюсы: Безопасный для третьих лиц, не нужно хранить пароли Минусы: Сложность реализации, зависит от провайдера

5. OpenID Connect (OIDC)

Расширение OAuth 2.0 для аутентификации:

from fastapi.security import OpenIdConnect

oidc_config = OpenIdConnect(
    openIdConnectUrl="https://auth.example.com/.well-known/openid-configuration"
)

@app.get("/protected")
async def protected(token: str = Depends(oidc_config)):
    # Проверка токена через OIDC провайдера
    return {"access": "granted"}

6. Session-Based Authentication

Традиционный метод с cookies:

from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from fastapi import Depends, HTTPException, status
import hashlib

# Хранилище сессий (в production используйте Redis)
sessions = {}

def create_session(username: str) -> str:
    """Создание сессии"""
    session_id = hashlib.sha256(f"{username}{datetime.now()}".encode()).hexdigest()
    sessions[session_id] = {"username": username, "created_at": datetime.now()}
    return session_id

def verify_session(session_id: str):
    """Проверка сессии"""
    if session_id not in sessions:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid session"
        )
    session = sessions[session_id]
    # Проверка срока действия
    if (datetime.now() - session["created_at"]).seconds > 3600:  # 1 час
        del sessions[session_id]
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Session expired"
        )
    return session

@app.post("/login")
async def login(username: str, password: str):
    # Проверка учетных данных (упрощено)
    session_id = create_session(username)
    return {"session_id": session_id}

@app.get("/protected")
async def protected(
    session_id: str = Cookie(None),
    session: dict = Depends(verify_session)
):
    return {"user": session["username"]}

Плюсы: Привычно, легко инвалидировать Минусы: Требует хранилища на сервере, сложнее масштабировать

7. Mutual TLS (mTLS)

Аутентификация на уровне сертификатов:

import ssl
from fastapi import FastAPI

app = FastAPI()

# Конфиг SSL/TLS
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_context.load_cert_chain(
    certfile="/path/to/server.crt",
    keyfile="/path/to/server.key"
)
ssl_context.load_verify_locations("/path/to/ca.crt")
ssl_context.verify_mode = ssl.CERT_REQUIRED

# Запуск сервера
# uvicorn main:app --ssl-keyfile=/path/to/key.pem --ssl-certfile=/path/to/cert.pem

Плюсы: Максимальная безопасность, шифрование Минусы: Сложность, требует управления сертификатами

Сравнение методов

МетодБезопасностьПростотаМасштабируемостьИспользование
Basic AuthНизкаяВысокаяПлохаяТестирование
API KeyСредняяВысокаяХорошаяМикросервисы
JWTВысокаяСредняяОтличнаяSPA, мобильные
OAuth 2.0ВысокаяНизкаяОтличнаяСоциальный вход
SessionСредняяСредняяПлохаяМонолитные приложения
mTLSОчень высокаяНизкаяХорошаяCritical systems

Best Practices

  • Всегда используйте HTTPS для передачи учетных данных
  • Хешируйте пароли с использованием bcrypt или Argon2
  • Не сохраняйте токены в localStorage, используйте cookies с httpOnly флагом
  • Установите правильный срок действия токенов
  • Используйте CSRF токены для POST запросов
  • Логируйте попытки входа (особенно неудачные)
  • Используйте двухфакторную аутентификацию (2FA) для критичных операций

Выбор метода зависит от архитектуры приложения и требований безопасности.

Какие методы аутентификации используются в абстрактных веб-сервисах? | PrepBro