Покрытие изображения Франк на Бессмысленно
В Предыдущая часть Мы создали пользовательскую модель пользователей в Django. В этой части я хотел бы показать, как катить пользовательскую аутентификацию. Ни настраиваемые пользовательские модели, ни пользовательские аутентификации не требуются для контроля доступа на основе ролей, но я хотел бы, чтобы эта серия была полной экскурсией по аутентификации и разрешению в Джангу. Код, сопровождающий серии, можно найти в Github Отказ Так что давайте начнем!
Аутентификация Django 101.
Аутентификация – это процесс выяснения, кто утверждает, что пользователь должен быть и проверки претензии. В системе аутентификации Django «низкий уровень» подход к проверке идентификации пользователя – звонить аутентифицировать от django.contrib.auth.authenticate . Эта функция проверяет идентификацию пользователя на каждом Аутентификация Backend настроен в Аутентификация_backends Переменная settings.py Отказ
По умолчанию Django использует Modelbackend. как единственная бэкэнда аутентификации. Поучительно изучить реализацию Modelbackend в Github :
class ModelBackend(BaseBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
if username is None or password is None:
return
try:
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
UserModel().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
...
Modelbackend Получает соответствующий пользователь от бэкэнда, используя либо данный имя пользователя или Username_field Определяется в пользовательской модели. Затем бэкэнда проверяет пароль, а также проверяет, может ли пользователь аутентифицироваться (проверяет, есть ли у пользователя IS_ Active Установлено на True ). Довольно просто, а?
Как мы будем аутентифицировать наших пользователей своим именем пользователя (E-mail) и паролем в этой серии, мы могли бы использовать Modelbackend Отказ Тем не менее, это поучительно написать нашу собственную бэкэнду. Кроме того, мы избавимся от всей ненужной котельной в Modelbackend Исходя из системы разрешения по умолчанию Django, которая нам не понадобится.
Пользовательская бэкэнда аутентификации
Каждая бэкэнда аутентификации в Джангу должна иметь методы аутентифицировать () и get_user () Отказ аутентифицировать () Метод должен проверить учетные данные, которые он получает и возвращает пользовательский объект, который соответствует этим учетным данным, если учетные данные действительны. Если учетные данные недействительны, это должно вернуть Нет Отказ
Вот простая реализация CheckPasswordbackend. :
# rbac/core/auth.py
import typing
from rbac.core.models import User
from rbac.core import services
class CheckPasswordBackend:
def authenticate(
self, request=None, email=None, password=None
) -> typing.Optional[User]:
user = services.find_user_by_email(email=email)
if user is None:
return None
return user if user.check_password(password) else None
def get_user(self, user_id) -> typing.Optional[User]:
try:
return User.objects.get(id=user_id)
except User.DoesNotExist:
return None
Мы используем services.find_user_by_email Способ создан в предыдущем посте для получения пользователя по электронной почте. Если пароль совпадает, мы возвращаем соответствующего пользователя. Вот и все! Давайте установим Django, чтобы использовать эту бэкэнду для аутентификации:
# rbac/settings.py AUTHENTICATION_BACKENDS = ["rbac.core.auth.CheckPasswordBackend"]
Теперь, когда мы называем аутентифицировать от django.contrib.auth мы по сути, называем аутентифицировать () от CheckPasswordBackend Отказ
Почему мы также определяли get_user выше в CheckPasswordBackend ? Это очень хороший вопрос. Ответ заключается в том, что документация Django говорит, что она должна быть реализована, но я понятия не имею, почему. Пожалуйста, бросьте комментарий, если вы знаете!
Итак, теперь у нас есть отличная новая бэкэнда аутентификации, как мы на самом деле используем его? Мы пишем вид AUTH/LOCK Это позволяет пользователям войти в систему с их электронной почтой и паролем. Если идентификация пользователя проверена, мы входим в систему, вызов Войти () Отказ Это создает сеанс для пользователя и хранит SessionId В файле cookie, позволяющее пользователю выполнять аутентифицированные запросы.
Перед реализацией Вход Вид, давайте будем ответственными разработчиками и писать тесты.
Тесты
Чтобы проверить логин, нам нужно создать пример пользователя. Мы делаем это в pteest Приспособление:
# tests/test_views.py
import pytest
from rbac.core.services import create_user
TEST_USER_NAME = "Jane Doe"
TEST_USER_EMAIL = "jane@example.org"
TEST_USER_PASSWORD = "aösdkfjgösdgäs"
@pytest.fixture
def sample_user():
user = create_user(
name=TEST_USER_NAME, email=TEST_USER_EMAIL, password=TEST_USER_PASSWORD
)
return user
Теперь давайте будем использовать этот приспособлений в два теста. Первый тест проверяет, что вход с неверным возвратом пароля 401:
from django.test import Client
@pytest.mark.django_db
def test_login_fails_with_invalid_credentials(sample_user):
client = Client()
response = client.post(
"/auth/login",
dict(email=TEST_USER_EMAIL, password="wrong-password"),
content_type="application/json",
)
assert response.status_code == 401
assert "sessionid" not in client.cookies
Мы используем Django Test Client Для создания запросов от тестов без фактического запуска сервера.
Второй тест проверяет, что логин преуспевает с действительными учетными данными:
@pytest.mark.django_db
def test_login_succeeds_with_valid_credentials(sample_user):
client = Client()
assert "sessionid" not in client.cookies
response = client.post(
"/auth/login",
dict(email=TEST_USER_EMAIL, password=TEST_USER_PASSWORD),
content_type="application/json",
)
assert response.status_code == 200
assert "sessionid" in client.cookies
На данный момент мы можем начать Pтобы-часы С помощью команды PTW - Тесты/test_views.py и код до прохода испытаний. Если вы еще не добавили Pтобы-часы к требования - dev.txt Тем не менее, вы должны сделать это сейчас.
Вход в систему
Давайте теперь добавим представление для входа в систему пользователя. Мы ожидаем, что пользователи публикуют их электронную почту и пароль в корпусе запроса JSON. Вот как мы разбираем тело, аутентифицируйте пользователя и входите в систему:
# rbac/core/auth.py
import json
from django.contrib.auth import authenticate, login
from django.http import HttpResponse
from django.views.decorators.http import require_http_methods
@require_http_methods(["POST"])
def login_view(request):
body = json.loads(request.body.decode())
user = authenticate(request, email=body["email"], password=body["password"])
if user:
login(request, user)
return HttpResponse("OK")
else:
return HttpResponse("Unauthorized", status=401)
Если звонок на аутентифицировать Возвращает действительный пользователь, войти в систему пользователя, создать сеанс и установить файл cookie сеанса. В противном случае мы возвращаем 401.
Теперь нам нужно определить конечную точку для нашего мнения:
# rbac/core/auth.py
from django.urls.conf import re_path
urlpatterns = [
re_path("^login$", login_view),
]
Нам также нужно определить новый маршрут с именем auth в RBAC/URLS.PY. :
# rbac/core/urls.py
from django.conf.urls import include, re_path
from rbac.core import views
urlpatterns = [
re_path(r"^$", views.index),
re_path(r"^auth/", include("rbac.core.auth")),
]
Со всеми это сделано, ваши тесты должны проходить с летающими цветами.
Поздравляем, теперь вы должны иметь гораздо более глубокое понимание того, как работает аутентификация в Джангу! Пожалуйста, оставьте комментарий Как вам понравилась статья. В следующих частях мы будем работать в направлении контроля доступа на основе ролей. Увидимся в следующий раз!
Оригинал: “https://dev.to/ksaaskil/custom-user-authentication-in-django-with-tests-34em”