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

花韻仙語
发布: 2025-11-20 12:41:00
原创
788人浏览过

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

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

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

1. 定义自定义用户模型

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

# your_app_name/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
from 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.py
AUTH_USER_MODEL = 'your_app_name.CustomUser'
登录后复制

3. 创建自定义认证后端

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

# your_app_name/backends.py
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from 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列表中。通常,自定义后端应放在默认后端之前,以便优先处理。

AI Word
AI Word

一款强大的 AI 智能内容创作平台,致力于帮助用户高效生成高质量、原创且符合 SEO 规范的各类文章。

AI Word 226
查看详情 AI Word
# your_project_name/settings.py
AUTHENTICATION_BACKENDS = [
    'your_app_name.backends.EmailBackend', # 你的自定义后端
    'django.contrib.auth.backends.ModelBackend', # Django默认的ModelBackend
]
登录后复制

5. 定义登录序列化器

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

# your_app_name/serializers.py
from rest_framework import serializers
from .models import CustomUser

class 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.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.authtoken.models import Token
from django.contrib.auth import authenticate # 导入Django的authenticate函数

from .serializers import LoginSerializer
from .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.py
from django.urls import path
from .views import LoginAPIView

urlpatterns = [
    path('login/', LoginAPIView.as_view(), name='login'),
]

# your_project_name/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    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自定义用户模型实现邮箱登录认证教程的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号