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

Как описывал метод проверки Bearer токена?

1.7 Middle🔥 251 комментариев
#REST API и HTTP#Безопасность

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

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

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

Проверка Bearer токена: методы и реализация

Bearer токены — это стандартный механизм аутентификации в REST API. Это важная тема для backend разработчика.

Что такое Bearer токен

Bearer токен — это строка, которая передаётся в заголовке Authorization:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Слово Bearer означает носитель — приложение несёт этот токен в запросе.

Первый метод: Ручная проверка в каждом view

from flask import Flask, request, jsonify
from functools import wraps

app = Flask(__name__)

def get_bearer_token(request):
    auth_header = request.headers.get('Authorization', '')
    if not auth_header.startswith('Bearer '):
        return None
    return auth_header[7:]

def verify_token(token: str) -> bool:
    valid_tokens = ['token123', 'token456']
    return token in valid_tokens

@app.route('/api/protected', methods=['GET'])
def protected():
    token = get_bearer_token(request)
    if not token:
        return jsonify({'error': 'No token'}), 401
    if not verify_token(token):
        return jsonify({'error': 'Invalid token'}), 401
    return jsonify({'data': 'Secret info'})

Проблема: код повторяется везде. Нарушается DRY.

Второй метод: Декоратор для защиты (лучше)

from functools import wraps

def require_bearer_token(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        token = get_bearer_token(request)
        if not token:
            return jsonify({'error': 'Missing token'}), 401
        if not verify_token(token):
            return jsonify({'error': 'Invalid token'}), 401
        return f(token=token, *args, **kwargs)
    return decorated_function

@app.route('/api/protected')
@require_bearer_token
def protected(token):
    return jsonify({'data': 'Secret info'})

@app.route('/api/admin')
@require_bearer_token
def admin(token):
    return jsonify({'admin': True})

Логика проверки в одном месте.

Третий метод: JWT токены с PyJWT

Это производственный подход. JWT токены самовалидирующиеся — содержат подпись.

import jwt
from datetime import datetime, timedelta

SECRET_KEY = 'super-secret-key'
ALGORITHM = 'HS256'

def create_jwt_token(user_id: int, expires_in: int = 3600) -> str:
    payload = {
        'user_id': user_id,
        'exp': datetime.utcnow() + timedelta(seconds=expires_in),
        'iat': datetime.utcnow(),
    }
    return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)

def verify_jwt_token(token: str) -> dict:
    try:
        return jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
    except jwt.ExpiredSignatureError:
        raise jwt.InvalidTokenError('Token expired')
    except jwt.InvalidTokenError as e:
        raise jwt.InvalidTokenError(f'Invalid: {str(e)}')

def require_jwt(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        token = get_bearer_token(request)
        if not token:
            return jsonify({'error': 'Missing token'}), 401
        try:
            payload = verify_jwt_token(token)
        except jwt.InvalidTokenError as e:
            return jsonify({'error': str(e)}), 401
        return f(payload=payload, *args, **kwargs)
    return decorated_function

@app.route('/api/login', methods=['POST'])
def login():
    user_id = 1
    token = create_jwt_token(user_id)
    return jsonify({'access_token': token, 'token_type': 'Bearer'})

@app.route('/api/profile')
@require_jwt
def get_profile(payload):
    user_id = payload['user_id']
    return jsonify({'user_id': user_id, 'name': 'Alice'})

Четвёртый метод: Django REST Framework

from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication

@api_view(['GET'])
@permission_classes([IsAuthenticated])
@authentication_classes([TokenAuthentication])
def protected_view(request):
    return Response({'message': f'Hello {request.user}'})

Или для JWT:

from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.authentication import JWTAuthentication

@api_view(['GET'])
@permission_classes([IsAuthenticated])
@authentication_classes([JWTAuthentication])
def protected_view(request):
    return Response({'user': request.user.username})

Пятый метод: FastAPI (современный)

from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthCredentials

app = FastAPI()
security = HTTPBearer()

def verify_bearer_token(credentials: HTTPAuthCredentials = Depends(security)) -> str:
    token = credentials.credentials
    if not verify_token(token):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail='Invalid',
            headers={'WWW-Authenticate': 'Bearer'},
        )
    return token

@app.get('/protected')
def protected(token: str = Depends(verify_bearer_token)):
    return {'data': 'secret'}

Практический пример: Система с refresh токенами

from datetime import datetime, timedelta
import jwt

class TokenManager:
    def __init__(self, secret_key: str):
        self.secret_key = secret_key
        self.algorithm = 'HS256'
    
    def create_tokens(self, user_id: int):
        now = datetime.utcnow()
        
        access_payload = {
            'user_id': user_id,
            'type': 'access',
            'exp': now + timedelta(minutes=15),
            'iat': now,
        }
        
        refresh_payload = {
            'user_id': user_id,
            'type': 'refresh',
            'exp': now + timedelta(days=30),
            'iat': now,
        }
        
        access_token = jwt.encode(access_payload, self.secret_key, algorithm=self.algorithm)
        refresh_token = jwt.encode(refresh_payload, self.secret_key, algorithm=self.algorithm)
        
        return {
            'access_token': access_token,
            'refresh_token': refresh_token,
            'token_type': 'Bearer',
        }
    
    def verify_token(self, token: str, token_type: str = 'access'):
        try:
            payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
            if payload.get('type') != token_type:
                raise jwt.InvalidTokenError(f'Expected {token_type} token')
            return payload
        except jwt.ExpiredSignatureError:
            raise jwt.InvalidTokenError('Token expired')
        except jwt.InvalidTokenError as e:
            raise jwt.InvalidTokenError(f'Invalid: {str(e)}')

manager = TokenManager(secret_key='my-secret-key')
tokens = manager.create_tokens(user_id=1)
payload = manager.verify_token(tokens['access_token'], token_type='access')

Типичные ошибки

1. Не проверять тип токена:

# Плохо
def verify_token(token):
    return jwt.decode(token, key, algorithms=['HS256'])

# Хорошо
def verify_token(token, expected_type='access'):
    payload = jwt.decode(token, key, algorithms=['HS256'])
    if payload.get('type') != expected_type:
        raise InvalidTokenError('Wrong type')
    return payload

2. Не проверять истечение: JWT проверяет exp, но если custom токены — проверяй сам.

3. Передавать secret в коде:

# Плохо
SECRET = 'my-secret-key'

# Хорошо
SECRET = os.getenv('SECRET_KEY')

4. Не логировать попытки входа:

def require_jwt(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = get_bearer_token(request)
        try:
            payload = verify_jwt_token(token)
        except Exception as e:
            logger.warning(f'Auth failed: {e}')
            return jsonify({'error': 'Invalid'}), 401
        return f(payload=payload, *args, **kwargs)
    return decorated

Итоговая рекомендация

  • В production: используй JWT токены с refresh механизмом
  • Всегда проверяй: expiration, тип, подпись
  • Логируй попытки: для security audit
  • Используй HTTPS: Bearer токены должны передаваться по HTTPS
  • Храни секреты безопасно: в environment variables
Как описывал метод проверки Bearer токена? | PrepBro