# Как пользователь попадает в 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 про куки:

Мы можем использовать куки, чтобы аутентифицировать пользователя.
1. Спросим у пользователя пароль один раз, когда он залогинится.
2. Придумаем для пользователя специальную секретную строку (обычно её называют id сессии пользователя или session_id)
3. Запишем session_id и user_id в БД
4. Вернём браузеру cookie с session_id
После этого браузер будет отправлять в запросе cookie с session_id, а мы сможем по session_id определить user_id и достать пользователя из базы.

Таким образом мы не храним пароль в браузере и не спрашиваем его каждый раз у пользователя. 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)