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

Как происходит авторизация?

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

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

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

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

Как происходит авторизация

Авторизация — это процесс определения того, какие действия может выполнять аутентифицированный пользователь. Это отличается от аутентификации, которая проверяет личность пользователя.

1. Основной процесс авторизации

Авторизация обычно происходит в три этапа:

# 1. Аутентификация — кто ты?
user = authenticate(username='john', password='secret')

# 2. Авторизация — что ты можешь делать?
if user and user.has_perm('posts.delete_post'):
    # 3. Выполнение действия
    post.delete()
else:
    raise PermissionError('Нет прав на удаление поста')

2. Авторизация на основе ролей (RBAC)

Это самый распространённый подход — пользователь имеет роль, роль имеет набор прав.

from django.contrib.auth.models import User, Group, Permission
from django.contrib.contenttypes.models import ContentType

# Создаём роли и права
class User(models.Model):
    username = models.CharField(max_length=100)
    role = models.ForeignKey(Role, on_delete=models.CASCADE)

class Role(models.Model):
    name = models.CharField(max_length=100)  # 'admin', 'moderator', 'user'
    permissions = models.ManyToManyField(Permission)

# Проверка прав
def can_delete_post(user, post):
    return user.role.permissions.filter(codename='delete_post').exists()

# Использование
if can_delete_post(request.user, post):
    post.delete()

3. Авторизация на основе атрибутов (ABAC)

Права зависят от атрибутов пользователя, ресурса и контекста.

class Post(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    is_published = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

def can_edit_post(user, post):
    # ABAC логика
    checks = [
        user.id == post.author_id,  # автор может редактировать
        user.role.name == 'admin',  # админ может всё
        (user.role.name == 'moderator' and post.is_published),  # модератор — опубликованные
        (datetime.now() - post.created_at).days < 30,  # можно редактировать 30 дней
    ]
    return any(checks)

4. Django встроенная система прав

from django.contrib.auth.decorators import permission_required
from django.views.decorators.http import require_http_methods

# Декоратор на функцию представления
@permission_required('posts.delete_post')
def delete_post(request, post_id):
    post = Post.objects.get(id=post_id)
    post.delete()
    return redirect('posts:list')

# В классе представления
from django.contrib.auth.mixins import PermissionRequiredMixin

class DeletePostView(PermissionRequiredMixin, DeleteView):
    model = Post
    permission_required = 'posts.delete_post'
    success_url = reverse_lazy('posts:list')

# Проверка в представлении
if request.user.has_perm('posts.view_post'):
    # показываем пост
    pass

# Проверка на уровне QuerySet (фильтрация)
posts = Post.objects.filter(
    Q(author=request.user) |  # свои посты
    Q(is_published=True, author__role__name='admin')  # опубликованные админа
)

5. API авторизация (Token/JWT)

Token Based Auth

from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated

# Получить токен
from rest_framework.authtoken.views import obtain_auth_token

# settings.py
INSTALLED_APPS = ['rest_framework.authtoken']
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.TokenAuthentication'],
}

# Использование в API
@api_view(['GET'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def get_profile(request):
    return Response({'user': request.user.username})

# Client отправляет токен
# curl -H 'Authorization: Token 12345abcde' http://api.example.com/profile/

JWT (JSON Web Token)

from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework.permissions import IsAuthenticated

# settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework_simplejwt.authentication.JWTAuthentication'],
}

# urls.py
urlpatterns = [
    path('api/token/', TokenObtainPairView.as_view()),  # POST вернёт access + refresh токены
    path('api/token/refresh/', TokenRefreshView.as_view()),  # обновить access
]

# Использование
@api_view(['POST'])
@authentication_classes([JWTAuthentication])
@permission_classes([IsAuthenticated])
def protected_view(request):
    return Response({'message': 'Вы авторизованы'})

# Client отправляет JWT
# curl -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc...' http://api.example.com/protected/

6. OAuth 2.0 авторизация (через внешних поставщиков)

from social_django.models import UserSocialAuth
from allauth.socialaccount.models import SocialAccount

# settings.py
INSTALLED_APPS = [
    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'allauth.socialaccount.providers.google',
    'allauth.socialaccount.providers.github',
]

# Пользователь авторизуется через Google
# 1. На фронте перенаправляем на Google
# 2. Google возвращает код
# 3. Мы обмениваем код на токен
# 4. Получаем информацию пользователя
# 5. Создаём или обновляем пользователя в нашей БД

def google_auth_callback(request):
    code = request.GET.get('code')
    # Обмениваем code на токен
    token_response = requests.post(
        'https://oauth2.googleapis.com/token',
        data={
            'client_id': settings.GOOGLE_CLIENT_ID,
            'client_secret': settings.GOOGLE_CLIENT_SECRET,
            'code': code,
            'redirect_uri': 'http://localhost:8000/auth/callback/'
        }
    )
    access_token = token_response.json()['access_token']
    
    # Получаем данные пользователя
    user_info = requests.get(
        'https://www.googleapis.com/oauth2/v1/userinfo',
        headers={'Authorization': f'Bearer {access_token}'}
    ).json()
    
    # Создаём или обновляем пользователя
    user, created = User.objects.get_or_create(
        email=user_info['email'],
        defaults={'username': user_info['given_name']}
    )
    
    # Логируем пользователя
    request.session['user_id'] = user.id
    return redirect('home')

7. Кастомная авторизация с декораторами

from functools import wraps
from django.http import HttpResponseForbidden

def require_role(role_name):
    """Проверяет наличие роли у пользователя"""
    def decorator(view_func):
        @wraps(view_func)
        def wrapper(request, *args, **kwargs):
            if not request.user.is_authenticated:
                return redirect('login')
            if request.user.role.name != role_name:
                return HttpResponseForbidden('Access denied')
            return view_func(request, *args, **kwargs)
        return wrapper
    return decorator

def require_any_role(*role_names):
    """Проверяет что роль пользователя входит в список"""
    def decorator(view_func):
        @wraps(view_func)
        def wrapper(request, *args, **kwargs):
            if not request.user.is_authenticated:
                return redirect('login')
            if request.user.role.name not in role_names:
                return HttpResponseForbidden('Access denied')
            return view_func(request, *args, **kwargs)
        return wrapper
    return decorator

# Использование
@require_role('admin')
def admin_panel(request):
    return render(request, 'admin/panel.html')

@require_any_role('admin', 'moderator')
def moderate_posts(request):
    return render(request, 'admin/posts.html')

8. Авторизация на уровне QuerySet

class PostQuerySet(models.QuerySet):
    def for_user(self, user):
        """Фильтрует посты в зависимости от прав пользователя"""
        if user.is_superuser:
            return self
        if user.role.name == 'admin':
            return self
        if user.role.name == 'moderator':
            return self.filter(is_published=True)
        # Обычный пользователь видит только свои посты и опубликованные
        return self.filter(
            Q(author=user) | Q(is_published=True)
        )

class Post(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    is_published = models.BooleanField(default=False)
    
    objects = PostQuerySet.as_manager()

# Использование
posts = Post.objects.for_user(request.user)  # автоматическая фильтрация

9. Авторизация в Django REST Framework

from rest_framework.permissions import BasePermission

class IsPostAuthor(BasePermission):
    """Только автор может редактировать пост"""
    def has_object_permission(self, request, view, obj):
        return obj.author == request.user

class IsAdminOrReadOnly(BasePermission):
    """Админ может писать, остальные только читать"""
    def has_permission(self, request, view):
        if request.method in ['GET', 'HEAD', 'OPTIONS']:
            return True
        return request.user and request.user.is_staff

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    permission_classes = [IsAdminOrReadOnly, IsPostAuthor]
    
    def get_queryset(self):
        # Фильтруем посты по правам
        return Post.objects.for_user(self.request.user)

Мои рекомендации

  • Используй Django встроенные системы для простых случаев
  • Для API — JWT токены вместо session cookies
  • Проверяй права на уровне QuerySet чтобы не утекли данные
  • Используй классные permissions в DRF (не проверяй в view)
  • Логируй попытки несанкционированного доступа для безопасности
  • Кешируй результаты проверки прав если они дорогие
  • RBAC простое и понятное — используй его по умолчанию
Как происходит авторизация? | PrepBro