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

Что будешь использовать для написания авторизации?

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)"
}

# Это обеспечивает:
# - Масштабируемость
# - Безопасность
# - Простоту
# - Современный подход
Что будешь использовать для написания авторизации? | PrepBro