AndreyMelnikov.MyBlog # мой блог: IT-марафон.

Все мои посты

Django. Авторизация и ограничение доступа.

Как мы уже ранее говорили, Аутентификация — это процесс подтверждения, что пользователь именно тот, за кого себя выдает, то есть, процесс сопоставления переданных данных от пользователя с существующими учетными данными в БД.
Авторизация — это процесс принятия решения о том, что именно этой аутентифицированной персоне разрешается делать, то есть, назначение прав пользователю для разграничения доступа к ресурсам. И, как раз, об ограничении доступа сегодня и пойдет речь.

Допустим в нашем проекте или приложении необходимо разграничить доступ различным пользователям к ресурсам. Например, у нас есть автотранспортные предприятия. У каждого предприятия есть свои сотрудники – пользователи. За каждым предприятием закреплены свои автомобили. Соответственно, мы создали модели предприятий, модели пользователей и модели автомобилей с соответствующими связями между таблицами БД. И нам нужно сделать так, чтобы сотрудник (пользователь) закрепленный за своим предприятием мог получить доступ к списку автомобилей только своего предприятия.
В данном случае удобно (дабы не изобретать велосипед) использовать стандартный функционал Django «из коробки», а именно использовать стандартный класс User для создания моделей сотрудников. Например, наши пользовательские классы Driver и Manager, мы сделаем наследниками класса User.

Ограничение доступа можно cделать на уровне представлений (views.py). Самый простой способ это реализовать – использовать модуль django.contrib.auth.
Тут стоит сделать уточнение, если наше view представляет из себя функцию, то для авторизации следует использовать соответствующий декоратор @login_required, предварительно импортировав его:

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    
    ...

Мы знаем, что декоратор проверяет условие (контракт) до того, как функция, обернутая в этот декоратор, будет выполнена. Поэтому с login_required логика следующая:

Если пользователь не авторизован, то будет перенаправлен на URL, указанный в параметре конфигурации settings.LOGIN_URL, передавая текущий абсолютный путь в запросе.
Если пользователь авторизован, то выполняет код представления (view). В коде представления не требуется выполнять проверку авторизован ли пользователь или нет.

В случае, если наше представление организовано в виде класса, тогда нам понадобиться соответствующий миксин LoginRequiredMixin. Импрортируем его из django.contrib.auth.mixins. Мы должны включить (подмешать) данный миксин в список наследования класса нашего представления. Кроме того, в теле класса мы должны переопределить стандартное поле данного миксина: raise_exeption = True, для случая, если пользователь не авторизован. Для него будет выброшена ошибка 403.

from django.contrib.auth.mixins import LoginRequiredMixin

class MyView(LoginRequiredMixin, generics.ListAPIView):
	
    ...
    raise_exception = True

Логика работы данного миксина (класса) схожа с логикой рассмотренного ранее декоратора:

Если пользователь авторизован, срабатывает редирект на указанный в параметре конфигурации settings.LOGIN_URL. Иначе, пользователь получает ошибку 403.
На самом деле, модуль auth из django.contrib имеет гораздо больший функционал, помимо того, что был описан в данной статье. И его рассмотрение, тема для отдельного поста. Но в рамках сегодняшней задачи, мы достаточно просто и быстро добились необходимого.
Таким образом, мы добиваемся того, что логика работы view будет ограничена проверкой, является ли пользователь аутентифицированным. Что позволяет нам добиться ограничения выполнения кода внутри view.

Мы описали только половину того, как можно решить нашу задачу. Напомню, нам необходимо, чтобы авторизованный пользователь (например, Manager) получал список именно тех автомобилей, которые принадлежат его предприятию.
Допустим, текущий пользователь авторизован и код внутри views будет выполнен. Таким образом, нам необходимо отфильтровать для данного авторизованного пользователя те автомобили, которые соответствуют его предприятию.
Данные о текущем пользователе можно получить из переменной request, которая автоматически передается при вызове view. Если мы используем функции в качестве представлений, то можно просто указать request в качестве входного параметра, и обращаться к ней внутри функции:

def my_view(request):

    user = request.user.username
    ...

Если в качестве организации наших представлений использованы классы, то придется переопределять родительский метод, для того чтобы получить доступ к self.request:

from ..models import Car, Enterprise, User, Manager


class CarListView(LoginRequiredMixin, generics.ListAPIView, ListModelMixin):
    # визуализатор list для модели Car
    def get_queryset(self):
        user_name = self.request.user.username
        enterprise =    Enterprise.objects.get(manager__username__contains=user_name)
        list_car = Car.objects.filter(of_enterprise=enterprise)
        return list_car
    queryset = get_queryset
    serializer_class = CarSerializer
    raise_exception = True  # переопределение поля исключения, в случае не аутентифицированного пользователя

На примере выше мы переопределили родительский метод get_queryset. Внутри метода обратились к данным текущего пользователя, user_name = self.request.user.username. Затем использовали запрос к модели Enterprise через objects.get с параметрами запроса полей через связную таблицу. В данном случае мы обращаемся к модели Enterprise, через связную (по первичному ключу) модель manager по полю username соответствующему значению username, которое мы вытащили из self.request. Затем, получив объект в виде enterprise, мы фильтруем по названию предприятия те автомобили, которые к нему привязаны, (list_car = Car.objects.filter(of_enterprise=enterprise) ), и возвращаем список найденных автомобилей в стандартное поле класса queryset.
Это достаточно тривиальный пример, который показывает логику запросов и фильтраций.

Теперь каждый «залогиненый» пользователь (manager), при обращении к визуализатору списка автомобилей, будет получать список только тех автомобилей, которые закреплены за его предприятием. Тем самым, мы добились разграничения доступа пользователей к ресурсам.

Назад к списку постов