使用 Django、Djoser 和 JWT 进行用户身份验证的分步指南:第一部分

用户身份验证和管理是启动新项目时的基础功能。由于这些任务经常重复,因此已经开发了各种软件包来简化流程,使开发人员可以专注于项目的其他方面。其中一个软件包是 Djoser,它与 Django REST Framework (DRF) 无缝集成以处理身份验证和用户管理。在本指南中,我将指导您使用 Djoser 构建完整的用户身份验证系统,包括设置电子邮件功能和自定义电子邮件模板。

设置项目

首先为您的项目创建一个目录:

mkdir userauth

导航到您首选 IDE 中的新目录,设置虚拟环境并激活它

python venv .venv
source .venv/bin/activate

接下来安装必要的软件包:

pip install django djangorestframework djoser djangorestframework_simplejwt social-auth-app-django drf-yasg

注意:某些依赖项(例如 social-auth-app-django)可能会随 Djoser 自动安装。如果是这样,您可以跳过明确添加它们的步骤。

安装后,生成一个 requirements.txt 文件来跟踪您的依赖项:

pip freeze > requirements.txt

您应该会看到 requirements.txt 文件中列出的所有已安装的包,包括任何依赖项。

创建 Django 项目并运行服务器

django-admin startapp userauth .

这将创建 django 项目。然后我们需要在项目中创建一个应用程序

python manage.py startapp accounts

您的项目目录现在应该包含以下内容:

  • .venv(虚拟环境)
  • accounts/(身份验证应用程序)
  • userauth/(主项目文件夹)
  • 管理.py
  • 要求.txt
  • 配置项目

    将所需的包和应用程序添加到 settings.py 中的 INSTALLED_APPS 部分:

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
    
         # Third Party Apps
        'rest_framework',
        'djoser',
        'rest_framework_simplejwt',
        'drf_yasg',
    
        # Local Apps
        'accounts',
    ]

    更新 settings.py 以包含 Django REST Framework 和 SimpleJWT 的配置:

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework_simplejwt.authentication.JWTAuthentication',
        ),
    }

    让我们创建自定义用户模型

    在 accounts 文件夹中创建 manager.py 文件

    账户/managers.py

    from django.contrib.auth.models import BaseUserManager
    
    
    class CustomUserManager(BaseUserManager):
    
        def create_user(self, email, username, password=None, **extra_fields) -> None:
    
            if not username:
                raise ValueError("Username is required")
    
            if not email:
                raise ValueError("Email is required")
    
    
            email = self.normalize_email(email)
            user = self.model(email=email, username=username, **extra_fields)
            user.set_password(password)
            user.save()
            return user
    
    
        def create_superuser(self, email, username, password, **extra_fields):
            """
            Create and save a SuperUser with the given email and password.
            """
            extra_fields.setdefault("is_staff", True)
            extra_fields.setdefault("is_superuser", True)
            extra_fields.setdefault("is_active", True)
    
            if extra_fields.get("is_staff") is not True:
                raise ValueError("Superuser must have is_staff=True.")
    
            if extra_fields.get("is_superuser") is not True:
                raise ValueError("Superuser must have is_superuser=True.")
    
            return self.create_user(email, username, password, **extra_fields)

    账户/模型.py

    from django.db import models
    from django.contrib.auth.models import AbstractUser
    from accounts.managers import CustomUserManager
    
    
    
    class CustomUser(AbstractUser):
        username = None
        email = models.EmailField(unique=True)
        is_verified = models.BooleanField(default=False)
    
    
        USERNAME_FIELD = 'email'
        REQUIRED_FIELDS = []
    
        objects = CustomUserManager()
    
    
        def __str__(self):
            return self.email

    在您的 settings.py 文件中添加此行

    AUTH_USER_MODEL = 'accounts.CustomUser'

    我们可以继续进行迁移,然后运行本地开发服务器

    python manage.py makemigrations
    python manage.py migrate
    python manage.py runserver

    到目前为止应该没有任何问题。

    配置 Djoser URL

    在您的项目的 URL 模式中包含 Djoser 提供的 URL,以及用于 API 文档的 Swagger:

    用户认证/urls.py

    from django.contrib import admin
    from django.urls import include, path
    from rest_framework import permissions
    from drf_yasg.views import get_schema_view
    from drf_yasg import openapi
    
    
    schema_view = get_schema_view(
        openapi.Info(
            title="User Accounts API",
            default_version="v1",
            description="REST implementation of Django authentication system using Djoser",
            contact=openapi.Contact(email="contact@snippets.local"),
            license=openapi.License(name="BSD License"),
        ),
        public=True,
        permission_classes=(permissions.AllowAny,),
    )
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api/docs', schema_view.with_ui("swagger", cache_timeout=0), name="swagger-ui"),
        path('api/', include('djoser.urls')), 
        path('api/', include('djoser.urls.jwt'))
    ]

    在浏览器中访问http://127.0.0.1:8000/api/docs/查看API文档。

    配置 Djoser 设置

    可以在此处找到可以为 djoser 配置的所有设置 Djoser 设置

    用户认证/settings.py

    DJOSER = {
        "LOGIN_FIELD": "email",
        "SEND_ACTIVATION_EMAIL": True,
        "ACTIVATION_URL": "accounts/activation/{uid}/{token}/",
        "SEND_CONFIRMATION_EMAIL": True,
    }

    这里我们要求用户收到一封激活电子邮件。激活 URL 是发送到用户电子邮件供他们点击的链接。需要提取 token 和 uid,并将它们作为正文发送到项目中的激活路由的 post 请求

    配置电子邮件发送

    最后,我们需要配置电子邮件发送。我将使用 mailtrap 发送电子邮件。您可以选择将电子邮件发送到控制台或您选择的任何电子邮件服务。

    用于发送电子邮件到你的控制台

    EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"

    使用外部邮件服务

    EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
    EMAIL_HOST = 'sandbox.smtp.mailtrap.io'
    EMAIL_PORT = '2525'
    EMAIL_HOST_USER = 'email-username'
    EMAIL_HOST_PASSWORD = 'email-password'

    用正确的凭据替换占位符

    为了测试这一点,我们将使用 postman 来测试它。

    创建新用户

    Postman request

    然后激活电子邮件发送给用户

    Activation Email

    自定义电子邮件模板

    让我们稍微自定义一下电子邮件模板

    在帐户目录中创建一个模板文件夹,然后创建一个电子邮件文件夹,并继续在其中创建模板文件夹

    帐户/模板/电子邮件/activation_email.py

    我们定制了djoser自带的默认电子邮件

    {% load i18n %}
    
    {% block subject %}
    {% blocktrans %}Account activation{% endblocktrans %}
    {% endblock subject %}
    
    {% block text_body %}
    {% blocktrans %}You're receiving this email because you need to finish activation process on {{ site_name }}.{% endblocktrans %}
    
    {% trans "Please go to the following page to activate your account:" %}
    {{ protocol }}://{{ domain }}/{{ url|safe }}
    
    {% trans "Thanks for using our site!" %}
    
    {% blocktrans %}The {{ site_name }} team{% endblocktrans %}
    {% endblock text_body %}
    
    {% block html_body %}
    

    {{ site_name }}

    {% blocktrans %}You're receiving this email because you need to finish the activation process on {{ site_name }}.{% endblocktrans %}

    {% trans "Please go to the following page to activate your account:" %}

    {% trans "Activate Account" %}

    {% trans "Thanks for using our site!" %}

    {% blocktrans %}The {{ site_name }} team{% endblocktrans %}

    {% trans "If you did not request this email, you can safely ignore it." %}
    {% endblock html_body %}

    然后在 accounts 目录中创建 email.py 文件

    账户/email.py

    from djoser import email
    
    
    class ActivationEmail(email.ActivationEmail):
        template_name = 'email/activation_email.html'

    要在模板中自定义站点名称,请将此行添加到 djoser 设置中

    DJOSER = {
        "LOGIN_FIELD": "email",
        "EMAIL_FRONTEND_SITE_NAME": 'My App',  # Add this line
        "SEND_ACTIVATION_EMAIL": True,
        "ACTIVATION_URL": "accounts/activation/{uid}/{token}/",
        "SEND_CONFIRMATION_EMAIL": True,
        "EMAIL": {
            "activation": "accounts.email.ActivationEmail",
        },
    }

    电子邮件模板现在如下所示

    Customized email

    扩展激活视图

    作为本文的最后一部分,让我们进行电子邮件验证。

    我们首先在 accounts/views.py 中自定义激活视图:

    账户/views.py

    from rest_framework.response import Response
    from rest_framework import status
    from djoser.views import UserViewSet
    
    
    # Create your views here.
    
    
    class ActivationView(UserViewSet):
        def activation(self, request, *args, **kwargs):
            serializer = self.get_serializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            user = serializer.user
            user.is_verified = True
            user.save()
            super().activation(request, *args, **kwargs)
            return Response(
                {"message": "Account activated successfully and verified!"},
                status=status.HTTP_200_OK,
            )

    我们正在扩展 djoser 上的激活视图以对其进行自定义,并将用户模型上的 is_verified 字段设置为 true

    账户/urls.py

    from django.urls import path
    from accounts.views import ActivationView
    
    
    urlpatterns = [
        path("accounts/activation/", ActivationView.as_view({'post': 'activation'}), name="email-activation")
    ]

    项目级别的 Urls 文件

    用户认证/urls.py

    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api/docs/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
        path('api/', include('djoser.urls')), 
        path('api/', include('djoser.urls.jwt')),
        path('api/', include('accounts.urls'))   # Add this line
    ]

    为了测试这一点,请创建一个新的测试用户并单击发送到电子邮件的激活网址。

    您进入此页面,因为该 URL 在我们的项目中不存在

    Verify user

    从 url 中提取 uid 和 token,并向你在 accounts/urls.py 文件中定义的激活路由发出 post 请求

    从截图来看,我的路线是;

    http://127.0.0.1:8000/accounts/verify/MTY/cil456-aaf8331efb885f0b4412f35ce544648c/

    uid 是 MTY

    令牌是cil456-aaf8331efb885f0b4412f35ce544648c

    使用参数向激活端点发出发布请求

    Verify Post request

    本教程到此结束,您将了解如何使用 Djoser 设置用户身份验证。现在,您拥有一个功能齐全的身份验证系统,该系统具有电子邮件激活和可自定义的模板。在本系列的第二部分中,我们将探索社交身份验证,使用户能够使用 Google、Facebook 和 GitHub 等第三方服务进行注册和登录。敬请期待!