---
# System prepended metadata

title: Как пользователь попадает в request.user?

---

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