Какие методы аутентификации используются в абстрактных веб-сервисах?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Методы аутентификации в веб-сервисах
Аутентификация - это процесс проверки личности пользователя. Разберём основные методы, используемые в современных веб-приложениях.
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) для критичных операций
Выбор метода зависит от архитектуры приложения и требований безопасности.