Django REST Framework自定义用户模型实现邮箱登录认证教程

Django REST Framework自定义用户模型实现邮箱登录认证教程

本教程详细指导如何在django rest framework中使用自定义用户模型实现基于邮箱和密码的登录认证。文章涵盖自定义用户模型、自定义认证后端、登录序列化器和api视图的配置,并重点解析了认证后端中常见的`usermodel`引用错误及`authenticate`方法的正确返回逻辑,确保系统能够准确验证用户身份。

在Django项目中,尤其是在构建RESTful API时,经常需要使用自定义用户模型来满足特定的业务需求,例如使用邮箱而非默认的用户名进行登录。本教程将详细介绍如何配置一个基于邮箱和密码的登录系统,并解决在自定义认证后端中可能遇到的常见问题

1. 定义自定义用户模型

首先,我们需要一个自定义用户模型,它继承自AbstractBaseUser和PermissionsMixin,并将email字段设为唯一的用户名。

# your_app_name/models.pyfrom django.db import modelsfrom django.contrib.auth.models import AbstractBaseUser, PermissionsMixinfrom django.utils.translation import gettext_lazy as _from .managers import CustomUserManager # 假设你有一个自定义的用户管理器class CustomUser(AbstractBaseUser, PermissionsMixin):    email = models.EmailField(_("email address"), unique=True)    is_staff = models.BooleanField(default=False)    is_active = models.BooleanField(default=True)    date_joined = models.DateTimeField(auto_now_add=True)    is_company = models.BooleanField(blank=True, null=True)    USERNAME_FIELD = "email" # 指定使用email作为登录用户名    REQUIRED_FIELDS = [] # 对于AbstractBaseUser,email是USERNAME_FIELD,不需要在这里重复    objects = CustomUserManager() # 使用自定义的用户管理器    def __str__(self):        return self.email

注意事项:

USERNAME_FIELD = “email” 明确告诉Django使用email字段作为用户的唯一标识符进行认证。REQUIRED_FIELDS 列表为空,因为email已经是USERNAME_FIELD。你需要创建一个CustomUserManager来处理用户创建,通常它会继承BaseUserManager。

2. 配置Django使用自定义用户模型

在项目的settings.py文件中,你需要指定Django使用你的自定义用户模型。

# your_project_name/settings.pyAUTH_USER_MODEL = 'your_app_name.CustomUser'

3. 创建自定义认证后端

Django的authenticate函数依赖于AUTHENTICATION_BACKENDS设置中列出的认证后端。为了实现通过邮箱登录,我们需要创建一个自定义的认证后端。

# your_app_name/backends.pyfrom django.contrib.auth.backends import ModelBackendfrom django.contrib.auth import get_user_modelfrom django.db.models import Q, MultipleObjectsReturned# 动态获取当前项目配置的用户模型,这是最佳实践User = get_user_model()class EmailBackend(ModelBackend):    def authenticate(self, request, username=None, password=None, **kwargs):        try:            # 使用 Q 对象进行不区分大小写的邮箱匹配            user = User.objects.get(Q(email__iexact=username))        except User.DoesNotExist:            # 如果用户不存在,应返回 None,而不是尝试设置密码            return None        except MultipleObjectsReturned:            # 如果存在多个匹配的用户,通常返回第一个            return User.objects.filter(email=username).order_by('id').first()        else:            # 验证密码并检查用户是否可认证            if user.check_password(password) and self.user_can_authenticate(user):                return user        # 认证失败(如密码不匹配)时,也应返回 None        return None    def get_user(self, user_id):        try:            user = User.objects.get(pk=user_id)        except User.DoesNotExist:            return None        return user if self.user_can_authenticate(user) else None

关键修正和解释:

User = get_user_model(): 这是最重要的一点。在自定义认证后端中,应该使用django.contrib.auth.get_user_model()来动态获取在settings.AUTH_USER_MODEL中指定的自定义用户模型。直接使用UserModel是错误的,因为UserModel不是一个预定义的全局变量。except User.DoesNotExist: return None: 当通过邮箱找不到用户时,authenticate方法应该直接返回None,表示认证失败。原始代码中尝试在except块中调用UserModel().set_password(password)是错误的,因为它会在一个不存在的用户实例上操作,并且不会正确处理认证流程。Q(email__iexact=username): 使用Q对象和iexact进行不区分大小写的邮箱匹配,提高用户体验。return None: 确保在任何认证失败的情况下(例如密码不匹配),authenticate方法都返回None。

4. 在settings.py中注册自定义认证后端

在settings.py中,将你的自定义认证后端添加到AUTHENTICATION_BACKENDS列表中。通常,自定义后端应放在默认后端之前,以便优先处理。

# your_project_name/settings.pyAUTHENTICATION_BACKENDS = [    'your_app_name.backends.EmailBackend', # 你的自定义后端    'django.contrib.auth.backends.ModelBackend', # Django默认的ModelBackend]

5. 定义登录序列化器

登录序列化器用于验证请求中的邮箱和密码数据。

# your_app_name/serializers.pyfrom rest_framework import serializersfrom .models import CustomUserclass LoginSerializer(serializers.Serializer): # 继承 serializers.Serializer 即可    email = serializers.EmailField()    password = serializers.CharField(write_only=True) # 密码不应被序列化输出    # 如果需要,可以添加一个 validate 方法来验证邮箱是否存在,但 authenticate 方法会处理    # def validate(self, data):    #     email = data.get('email')    #     password = data.get('password')    #     if not email or not password:    #         raise serializers.ValidationError("Email and password are required.")    #     return data

注意事项:

继承serializers.Serializer而不是serializers.ModelSerializer更合适,因为你不是在创建或更新CustomUser实例,而只是验证登录凭据。password字段应设置为write_only=True,以防止密码在API响应中泄露。

6. 创建登录API视图

登录API视图将接收客户端发送的邮箱和密码,调用authenticate函数进行认证,并在成功后返回认证令牌。

# your_app_name/views.pyfrom rest_framework.views import APIViewfrom rest_framework.response import Responsefrom rest_framework import statusfrom rest_framework.authtoken.models import Tokenfrom django.contrib.auth import authenticate # 导入Django的authenticate函数from .serializers import LoginSerializerfrom .models import CustomUser # 导入CustomUser模型class LoginAPIView(APIView):    def post(self, request):        serializer = LoginSerializer(data=request.data)        if serializer.is_valid():            email = serializer.validated_data["email"]            password = serializer.validated_data["password"]            # 调用Django的authenticate函数,它会遍历所有注册的认证后端            user = authenticate(request, username=email, password=password)            if user is not None:                # 认证成功,获取或创建Token                token, created = Token.objects.get_or_create(user=user)                response_data = {                    "status": status.HTTP_200_OK,                    "message": "success",                    "data": {                        "Token": token.key                    }                }                return Response(response_data, status=status.HTTP_200_OK)            else:                # 认证失败                response_data = {                    "status": status.HTTP_401_UNAUTHORIZED,                    "message": "Invalid Email or Password",                }                return Response(response_data, status=status.HTTP_401_UNAUTHORIZED)        else:            # 序列化器验证失败            response_data = {                "status": status.HTTP_400_BAD_REQUEST,                "message": "Bad Request",                "data": serializer.errors            }            return Response(response_data, status=status.HTTP_400_BAD_REQUEST)

重要改进:

token, created = Token.objects.get_or_create(user=user): 推荐使用get_or_create来获取或创建用户的认证令牌。这比直接get更健壮,因为如果用户首次登录,可能还没有令牌。authenticate(request, username=email, password=password): Django的authenticate函数会根据AUTHENTICATION_BACKENDS的顺序,尝试所有注册的后端,直到找到一个能够认证成功的后端。

7. URL配置

最后,将登录视图添加到你的urls.py中。

# your_app_name/urls.pyfrom django.urls import pathfrom .views import LoginAPIViewurlpatterns = [    path('login/', LoginAPIView.as_view(), name='login'),]# your_project_name/urls.pyfrom django.contrib import adminfrom django.urls import path, includeurlpatterns = [    path('admin/', admin.site.urls),    path('api/', include('your_app_name.urls')),]

总结

通过以上步骤,你已经成功地在Django REST Framework中设置了一个使用自定义用户模型和邮箱/密码进行认证的登录系统。核心在于正确配置AUTH_USER_MODEL,并实现一个健壮的自定义认证后端,该后端能够通过get_user_model()获取用户模型,并在认证失败时返回None。同时,登录API视图应使用authenticate函数,并妥善处理认证成功和失败的情况,包括令牌的获取或创建。遵循这些最佳实践将确保你的认证系统安全、可靠且易于维护。

以上就是Django REST Framework自定义用户模型实现邮箱登录认证教程的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1381550.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月14日 23:03:30
下一篇 2025年12月14日 23:03:34

相关推荐

  • SQLAlchemy声明式风格下如何指定数据库表模式

    本文详细阐述了如何在sqlalchemy的声明式风格中,为数据库表指定特定的schema。通过利用模型类中的`__table_args__`属性,开发者可以设置`schema`参数,从而控制表在postgresql等支持schema的数据库中的命名空间归属。这使得表能够被创建到指定的schema而非…

    2025年12月14日
    000
  • 优化SQLite3并发访问:解决读写冲突与提升性能

    本文旨在解决sqlite3数据库在多进程并发读写场景下的性能瓶颈与数据访问冲突问题。通过深入探讨索引优化、启用wal(write-ahead log)模式、复用数据库连接和批量数据插入等核心策略,结合安全、高效的编程实践,如参数化查询和规范化异常处理,指导开发者构建更健壮、高效率的sqlite3应用…

    2025年12月14日
    000
  • 优化Pandas条件更新:解决布尔列比较的PyCharm警告与KeyError

    本文探讨在pandas dataframe中根据布尔列条件更新另一列值时遇到的常见问题。针对pycharm对`== true`的pep 8警告以及使用`is true`导致的`keyerror`,文章提供了使用`.eq()`方法进行元素级比较的专业解决方案,并解释了其原理,旨在帮助开发者编写更符合p…

    2025年12月14日
    000
  • 使用Python从LAION 5B等在线数据库高效获取指定类别图片教程

    本教程旨在指导开发者如何利用python,通过api调用从laion 5b等大型在线图像数据库高效获取指定类别的图片,而无需下载整个庞大的数据集。文章详细介绍了使用laion knn服务进行图像搜索和下载的步骤,包括必要的库、api请求参数配置、数据处理以及图片保存机制,为数据科学家和开发者提供了一…

    2025年12月14日
    000
  • Telethon 异步编程指南:正确获取用户信息与协程处理

    在使用 telethon 库开发 telegram 客户端时,尝试获取自身信息(如 `client.get_me()`)时,常会遇到 `attributeerror: ‘coroutine’ object has no attribute ‘stringify&#…

    2025年12月14日
    000
  • IntelliJ IDEA 文件类型识别与管理指南

    JetBrains IDEs,如IntelliJ IDEA,主要通过文件名扩展名或哈希bang行来识别文件类型。本文将深入探讨IDE内部的文件类型管理机制,并提供详细的步骤,指导用户如何手动覆盖单个文件的类型,以及如何在IDE设置中配置全局文件类型映射,从而确保代码获得正确的语法高亮、智能提示和运行…

    2025年12月14日
    000
  • 使用 vgamepad 库模拟手柄按键:正确操作指南

    本文深入探讨了python `vgamepad` 库在模拟虚拟手柄按键时的一个常见问题:直接使用整数进行按键操作无效。文章阐明了 `vgamepad` 库设计上要求使用预定义的 `xusb_button` 枚举常量来确保按键模拟的正确性,并提供了详细的解释、示例代码和最佳实践,帮助开发者避免常见错误…

    2025年12月14日
    000
  • Python异常链机制深度解析:理解raise from与__cause__

    本文深入探讨Python的异常链机制,解释当一个异常在处理另一个异常时如何自动关联。我们将通过具体示例分析`During handling of the above exception`的含义,揭示Python如何通过`__cause__`属性隐式维护异常之间的联系。此外,文章还将详细介绍`rais…

    2025年12月14日
    000
  • 异步协程中控制流与资源锁的精细化管理

    在复杂的异步操作链中,当需要在嵌套协程中返回一个可等待对象,并要求资源锁在最终操作完成后才释放时,传统的 `with` 语句上下文管理器无法满足需求。本文将深入探讨此问题,并提供一种通过显式锁管理和 `asyncio.Task` 的回调机制来确保资源正确释放的解决方案,从而实现控制流的灵活转移与资源…

    2025年12月14日
    000
  • Pandas DataFrame行求和:解决混合数据类型导致0值结果的问题

    本教程旨在解决pandas dataframe在对包含混合数据类型的行进行求和时,numeric_only=true参数失效并返回0值的问题。核心解决方案是利用pd.to_numeric函数的errors=’coerce’参数,将非数值型数据安全转换为nan,然后再进行行求和…

    2025年12月14日
    000
  • 解决Polars动态API注册与Python类型检查器的兼容性问题

    本文深入探讨了在使用polars的动态api注册功能(如`@pl.api.register_expr_namespace`)时,mypy和pyright等类型检查器报告`attr-defined`错误的问题。文章分析了问题的根本原因,即python静态类型系统无法识别运行时动态添加的属性。针对此问题…

    2025年12月14日
    000
  • Python文件操作:为文本行自动添加递增序列号

    本教程详细介绍了如何使用python向现有文本文件追加新数据时,自动为其添加递增的序列号。通过利用a+文件模式、文件指针定位及f-string格式化,我们能够高效地读取当前行数并生成带有零填充的序列号,确保数据记录的完整性和可追溯性。 在许多数据记录和日志管理场景中,为每一条新记录自动添加一个递增的…

    2025年12月14日
    000
  • Python 实时数据可视化教程:Matplotlib 与 Pygame 实践

    本教程旨在解决Python中实时数据可视化的问题,特别是在使用Matplotlib进行动态图表更新时可能遇到的挑战。文章将首先详细介绍如何利用Matplotlib的交互模式高效地绘制和更新实时数据图,包括常见陷阱与优化技巧。随后,将引入Pygame作为构建高度自定义、轻量级实时图表的替代方案,并提供…

    2025年12月14日
    000
  • Django Class-Based View中QuerySet的动态过滤实践

    本教程详细讲解了在django class-based view中如何根据用户id或外键动态过滤queryset。文章首先阐明了在模型管理器中进行请求相关过滤的局限性,随后重点介绍了在listview的`get_queryset`方法中实现动态筛选的正确姿态,并结合`loginrequiredmix…

    2025年12月14日
    000
  • 如何在Pydantic中实现类级别字段的不可变性

    pydantic的`allow_mutation`配置可确保模型实例字段的不可变性。然而,对于类级别的字段,该配置无效。本文将深入探讨如何利用自定义元类(metaclass)来拦截和阻止对pydantic模型类属性的直接修改,从而实现真正的类级别不可变性,并提醒使用此高级技术时需谨慎。 在Pydan…

    2025年12月14日
    000
  • 解决Django生产环境CSRF 403错误:Nginx HTTPS配置指南

    本文旨在解决Django应用在生产环境(Nginx + Gunicorn)中遇到的CSRF 403错误,特别是当DEBUG=True时显示的“Origin checking failed”问题。核心在于Django的CSRF_COOKIE_SECURE=True设置与Nginx未正确配置HTTPS代…

    2025年12月14日
    000
  • 优雅测试 Python input() 提示信息:解耦与实践

    本文探讨了在 `pytest` 中有效测试 `Python` `input()` 函数提示信息的方法。针对直接使用 `capsys` 或 `capfd` 捕获 `input()` 提示的局限性,文章提出了一种推荐的解决方案:将提示信息的生成逻辑从主函数中解耦,独立为一个可测试的函数。通过这种方式,可…

    2025年12月14日
    000
  • 深入理解二叉树等和分割问题

    本文旨在探讨如何判断一个二叉树是否可以通过移除一条边被分割成两个和相等的子树,并返回该和。文章首先分析了一种常见的递归解法及其潜在问题,提供了详细的修正方案,随后介绍了一种更高效的自底向上遍历算法,通过一次遍历收集所有子树和,从而在O(N)时间复杂度内解决问题,并提供了完整的Python实现代码和注…

    2025年12月14日
    000
  • Odoo Gevent 环境下 VSCode 远程调试断点不命中解决方案

    本文提供odoo在gevent环境下使用vscode进行远程调试时,断点无法命中的解决方案。核心问题源于debugpy与gevent_support=true的冲突。解决方案涉及修改vscode调试配置,移除gevent_support,并创建一个自定义python入口脚本。该脚本在debugpy启…

    2025年12月14日
    000
  • 解决Tkinter Menubutton菜单不显示问题:完整指南

    本教程详细探讨了tkinter中`menubutton`控件无法正确显示其关联`menu`的常见问题。核心在于理解`menu`与`menubutton`之间的正确父子关系和绑定机制。通过将`menu`创建为`menubutton`的子组件,并将其明确赋值给`menubutton`的`menu`选项,…

    2025年12月14日
    000

发表回复

登录后才能评论
关注微信