← Назад к вопросам
Что будешь использовать для написания авторизации?
1.2 Junior🔥 171 комментариев
#DevOps и инфраструктура#Django
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что будешь использовать для написания авторизации?
Для авторизации выбор зависит от типа приложения (веб API, мобильное приложение, микросервисы), требований к безопасности и архитектуры системы. Расскажу про несколько проверенных подходов.
1. JWT (JSON Web Tokens) — современный стандарт
Когда использовать: REST API, микросервисы, мобильные приложения.
# Реализация с использованием PyJWT
from datetime import datetime, timedelta, timezone
import jwt
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthCredentials
app = FastAPI()
security = HTTPBearer()
SECRET_KEY = "your-secret-key-change-in-production"
ALGORITHM = "HS256"
TOKEN_EXPIRATION_HOURS = 24
# 1. Создание JWT токена
def create_access_token(data: dict, expires_delta: timedelta = None):
"""Создать JWT токен"""
to_encode = data.copy()
if expires_delta:
expire = datetime.now(timezone.utc) + expires_delta
else:
expire = datetime.now(timezone.utc) + timedelta(hours=TOKEN_EXPIRATION_HOURS)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# 2. Верификация JWT токена
def verify_token(credentials: HTTPAuthCredentials = Depends(security)):
"""Проверить и декодировать JWT токен"""
token = credentials.credentials
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
user_id: str = payload.get("sub")
if user_id is None:
raise HTTPException(status_code=401, detail="Invalid token")
return {"user_id": user_id}
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token expired")
except jwt.InvalidTokenError:
raise HTTPException(status_code=401, detail="Invalid token")
# 3. Использование в роутах
@app.post("/login")
def login(username: str, password: str):
"""Эндпоинт логина"""
# Проверить пароль в БД (упрощено)
if verify_password(username, password):
access_token = create_access_token(data={"sub": username})
return {"access_token": access_token, "token_type": "bearer"}
else:
raise HTTPException(status_code=401, detail="Invalid credentials")
@app.get("/profile")
def get_profile(token_data: dict = Depends(verify_token)):
"""Защищённый эндпоинт"""
user_id = token_data["user_id"]
return {"user_id": user_id, "message": "Это ваш профиль"}
Плюсы JWT:
- ✅ Без сохранения состояния на сервере (stateless)
- ✅ Легко масштабировать (подходит для микросервисов)
- ✅ Может быть использован с разными доменами (CORS-friendly)
- ✅ Информация о пользователе прямо в токене
Минусы JWT:
- ❌ Сложнее отозвать токен (нужна blacklist)
- ❌ Больше данных в каждом запросе
2. Session-based (традиционный подход)
Когда использовать: Монолитные приложения Django, веб-приложения с одним доменом.
# Django Session Middleware (встроено)
# Файл: settings.py
INSTALLED_APPS = [
'django.contrib.sessions',
'django.contrib.auth',
]
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
]
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
SESSION_COOKIE_AGE = 86400 # 24 часа
SESSION_COOKIE_SECURE = True # Только HTTPS
SESSION_COOKIE_HTTPONLY = True # Недоступна для JavaScript
# Использование
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
def login_view(request):
"""View для логина"""
if request.method == 'POST':
username = request.POST['username']
password = request.POST['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user) # Создаёт session
return redirect('home')
return render(request, 'login.html')
@login_required
def profile_view(request):
"""Защищённый view"""
return render(request, 'profile.html', {'user': request.user})
def logout_view(request):
"""View для логаута"""
logout(request) # Удаляет session
return redirect('login')
Плюсы Session:
- ✅ Встроено в Django
- ✅ Легко отозвать сессию
- ✅ Меньше данных в клиенте
- ✅ Защита от CSRF атак встроена
Минусы Session:
- ❌ Требует хранения на сервере
- ❌ Сложно масштабировать (нужна shared storage)
- ❌ Привязана к одному домену
3. OAuth 2.0 и OpenID Connect
Когда использовать: Когда нужна интеграция с внешними провайдерами (Google, GitHub, Yandex).
# Использование социальной авторизации
from django.contrib.auth.models import User
from social_django.models import UserSocialAuth
# Нужно установить: pip install social-auth-app-django
# Конфиг в settings.py
AUTHENTICATION_BACKENDS = [
'social_core.backends.google.GoogleOAuth2',
'social_core.backends.github.GithubOAuth2',
]
SOCIAL_AUTH_PIPELINE = [
'social_core.pipeline.auth.auth_allowed',
'social_core.pipeline.auth.get_login_user',
'social_core.pipeline.user.get_username',
'social_core.pipeline.user.create_user',
'social_core.pipeline.social.associate_user',
'social_core.pipeline.social.load_extra_data',
]
# В template
# <a href="{% url 'social:begin' 'google-oauth2' %}">Login with Google</a>
Плюсы OAuth 2.0:
- ✅ Не нужно хранить пароли
- ✅ Интеграция с популярными провайдерами
- ✅ Безопасность (шифрование, обновляемые токены)
Минусы OAuth 2.0:
- ❌ Зависимость от внешних сервисов
- ❌ Сложнее в настройке
4. Refresh Tokens (двух-уровневая авторизация)
Когда использовать: Приложения с высокими требованиями к безопасности.
# Продвинутая реализация JWT с refresh токенами
from datetime import datetime, timedelta, timezone
import jwt
from typing import Optional
class TokenManager:
"""Менеджер для работы с access и refresh токенами"""
SECRET_KEY = "secret-key"
ACCESS_TOKEN_EXPIRATION = timedelta(minutes=15) # Короткоживущий
REFRESH_TOKEN_EXPIRATION = timedelta(days=7) # Долгоживущий
@staticmethod
def create_access_token(user_id: str) -> str:
"""Создать короткоживущий access токен"""
payload = {
"sub": user_id,
"exp": datetime.now(timezone.utc) + TokenManager.ACCESS_TOKEN_EXPIRATION,
"type": "access"
}
return jwt.encode(payload, TokenManager.SECRET_KEY, algorithm="HS256")
@staticmethod
def create_refresh_token(user_id: str) -> str:
"""Создать долгоживущий refresh токен"""
payload = {
"sub": user_id,
"exp": datetime.now(timezone.utc) + TokenManager.REFRESH_TOKEN_EXPIRATION,
"type": "refresh"
}
return jwt.encode(payload, TokenManager.SECRET_KEY, algorithm="HS256")
@staticmethod
def verify_token(token: str, token_type: str = "access") -> Optional[dict]:
"""Проверить токен"""
try:
payload = jwt.decode(token, TokenManager.SECRET_KEY, algorithms=["HS256"])
if payload.get("type") != token_type:
return None
return payload
except (jwt.ExpiredSignatureError, jwt.InvalidTokenError):
return None
# Использование
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.post("/token")
def get_tokens(username: str, password: str):
"""Эндпоинт для получения access и refresh токенов"""
# Проверить пароль
if verify_password(username, password):
user_id = get_user_id(username)
access_token = TokenManager.create_access_token(user_id)
refresh_token = TokenManager.create_refresh_token(user_id)
return {
"access_token": access_token,
"refresh_token": refresh_token,
"token_type": "bearer"
}
else:
raise HTTPException(status_code=401, detail="Invalid credentials")
@app.post("/refresh")
def refresh_access_token(refresh_token: str):
"""Эндпоинт для обновления access токена"""
payload = TokenManager.verify_token(refresh_token, token_type="refresh")
if payload is None:
raise HTTPException(status_code=401, detail="Invalid refresh token")
user_id = payload["sub"]
new_access_token = TokenManager.create_access_token(user_id)
return {"access_token": new_access_token, "token_type": "bearer"}
Плюсы двух-уровневой авторизации:
- ✅ Безопасность: access токен часто обновляется
- ✅ Можно отозвать refresh токен
- ✅ Балансировка: защита + удобство
5. Мой выбор для различных сценариев
scenarios = {
"REST API": {
"рекомендация": "JWT с refresh токенами",
"причина": "Масштабируемость, stateless, подходит для микросервисов",
"стек": "FastAPI + PyJWT + SQLAlchemy"
},
"Монолит Django": {
"рекомендация": "Session-based или JWT",
"причина": "Session встроена, но JWT лучше для API",
"стек": "Django + django-rest-framework"
},
"Мобильное приложение": {
"рекомендация": "JWT с refresh токенами",
"причина": "Нет cookies, простота, безопасность",
"стек": "FastAPI + PyJWT"
},
"Социальная авторизация": {
"рекомендация": "OAuth 2.0 + локальная сессия",
"причина": "Удобство пользователя, безопасность",
"стек": "Django + social-auth"
}
}
6. Лучшие практики безопасности
# Всегда следуй этим правилам!
best_practices = [
"✅ Никогда не храни пароли в plain text (используй bcrypt или argon2)",
"✅ Передавай токены только по HTTPS",
"✅ Используй HttpOnly cookies для токенов (если не SPA)",
"✅ Устанавливай правильное время жизни токенов",
"✅ Валидируй токены на каждом запросе",
"✅ Защищай от CSRF атак",
"✅ Логируй попытки входа",
"✅ Используй rate limiting для эндпоинтов логина",
"✅ Двухфакторная аутентификация (2FA) для критичных систем",
"✅ Регулярно обновляй секретные ключи"
]
# Пример защиты
from fastapi.middleware import trustedhost
from fastapi_limiter import FastAPILimiter
app.add_middleware(
trustedhost.TrustedHostMiddleware,
allowed_hosts=["yourdomain.com"],
)
# Rate limiting
@app.post("/login")
@limiter.limit("5/minute") # Максимум 5 попыток в минуту
def login(request: Request):
pass
Итоговая рекомендация
Для типичного REST API я выбираю:
my_choice = {
"primary_method": "JWT с refresh токенами",
"libraries": ["FastAPI", "PyJWT", "SQLAlchemy"],
"password_hashing": "bcrypt или argon2",
"rate_limiting": "redis с FastAPILimiter",
"2fa": "опционально (pyotp для TOTP)"
}
# Это обеспечивает:
# - Масштабируемость
# - Безопасность
# - Простоту
# - Современный подход