# Как пользователь попадает в request.user? ## Короткий ответ Django сам его туда кладёт, за это отвечают приложения `django.contrib.auth` и `django.contrib.sessions` и middleware `django.contrib.auth.middleware.AuthenticationMiddleware` и `django.contrib.sessions.middleware.SessionMiddleware`. Их можно увидеть в файле settings.py в нашем проекте. ## Длинный ответ Для начала, нужно разобраться с тем, что такое middleware. ### Middleware Иногда нам хочется выполнять какой-то код при каждом запросе (например, логировать запросы), но не хочется добавлять его в каждый view. Для этого в Django есть механизм, который называется middleware. Middleware — это такой код, который обрабатывает запрос и прокидывает его дальше, в следующий middleware или view. Middleware также могут обрабатывать ответы, которые возвращаются view. ``` клиент ↓ запрос ↑ middleware1 ↓ ↑ middleware2 ↓ ↑ ответ view ``` Процесс обработки запроса можно схематично представить так: ```python request = ... # получили запрос от пользователя middleware_1.process_request(request) # middleware_1 обработала запрос middleware_2.process_request(request) response = view(request) # здесь наш код middleware_2.process_response(response) middleware_1.process_response(response) # middleware_1 обработала ответ return response # возвращаем пользователю ответ ``` Middleware похожи на декораторы — они помогают нам выносить общую логику за пределы функции. Только декораторы могут обернуть любые функции, а middleware — только view. Через middleware проходят все запросы, которые мы обрабатываем. Если нам нужно обернуть только конкретный view, а не все сразу, лучше использовать декоратор. Подробнее про middleware: https://docs.djangoproject.com/en/3.0/topics/http/middleware/ ### request.user Django использует middleware для того, чтобы в каждый запрос добавить поле `user`. Это выглядит примерно так: ``` class AuthenticationMiddleware: def process_request(request): user = authenticate_user(request) # здесь мы аутентифицируем пользователя и достаём его из базы данных request.user = user ``` AuthenticationMiddleware получает пользователя из `authenticate_user` и кладёт его в объект `request`. После этого выполняется код нашей функции, в которой мы можем использовать `request.user`. ### Как реализовать `authenticate_user()` Теперь поговорим о том, как можно реализовать функцию, которая по запросу вернёт нам пользователя, который сделал этот запрос. > Процесс определения пользователя, который прислал запрос, называется _аутентификацией_ Как нам аутентифицировать пользователя, который делает запрос? Другими словами, как мы можем убедиться, что запрос нам прислал именно тот пользователь, о котором мы думаем? Как нам сопоставить запрос с id пользователя в нашей базе данных? Самый привычный способ аутентификации — по логину и паролю. Давайте с каждым запросом каким-то образом (сейчас неважно, каким) передавать логин и пароль пользователя. Тогда мы можем аутентифицировать пользователя так: ```python from django.contrib.auth.models import AnonymousUser from django.contrib.auth.models import User def authenticate_user(request): username = request.POST.get('username') # здесь мы передаем логин и пароль в POST-запросе, но это необязательно password = request.POST.get('password') try: user = User.objects.get(username=username) except User.DoesNotExist: # пользователь не существует, возвращаем анонимного пользователя return AnonymousUser() if user.check_password(password): # пароль подошел, возвращаем найденного пользователя return user # пароль не подошел, возвращаем анонимного пользователя return AnonymousUser() ``` `AnonymousUser()` — это специальный объект для представления анонимного пользователя. Если пользователь в запросе анонимный, значит нам не удалось аутентицифировать запрос. Аутентификация по логину и паролю имеет право на жизнь, но у этого способа есть проблема: нам приходится передавать логин и пароль с каждым запросом. Из этого следует, что нам нужно - либо сохранять пароль на клиенте (в браузере) - либо спрашивать пароль у пользователя при каждом запросе Первый способ небезопасный, а второй очень неудобный для пользователя — представьте, что Практикум на каждый клик просит у вас ввести пароль. ### Аутентификация с помощью cookies На помощь приходят cookies или куки — те самые, про которые показывает уведомления чуть ли не каждый сайт в интернете. Куки позволяют серверу сохранить немного информации в браузере пользователя Шпаргалка от Julia Evans про куки: ![](https://i.imgur.com/YARMbDX.png) Мы можем использовать куки, чтобы аутентифицировать пользователя. 1. Спросим у пользователя пароль один раз, когда он залогинится. 2. Придумаем для пользователя специальную секретную строку (обычно её называют id сессии пользователя или session_id) 3. Запишем session_id и user_id в БД 4. Вернём браузеру cookie с session_id После этого браузер будет отправлять в запросе cookie с session_id, а мы сможем по session_id определить user_id и достать пользователя из базы. ![](https://i.imgur.com/4qziDkc.png) Таким образом мы не храним пароль в браузере и не спрашиваем его каждый раз у пользователя. win-win! Система аутентификации в Django с помощью сессий работает несколько сложнее, я намеренно опустил много подробностей. Их не обязательно знать, если вы не собираетесь писать собственную систему аутентификации. Достаточно понимать, что `request.user` появляется после того, как `django.contrib.auth.middleware.AuthenticationMiddleware` обрабатывает запрос. ### Ссылки [Использование аутентификации в Django](https://docs.djangoproject.com/en/3.0/topics/auth/default/) [Middlewares в Django](https://docs.djangoproject.com/en/3.0/topics/http/middleware/) [Исходник AuthenticationMiddleware](https://github.com/django/django/blob/master/django/contrib/auth/middleware.py#L15-L23) [OWASP Session Management CheatSheet (хардкор)](https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html)
{"metaMigratedAt":"2023-06-15T11:19:08.912Z","metaMigratedFrom":"Content","title":"Как пользователь попадает в request.user?","breaks":true,"contributors":"[{\"id\":\"9d0566a0-0f83-4e1c-81c3-3179358c8c0b\",\"add\":10286,\"del\":4188}]"}
Expand menu