You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django Rest Framework中启用邮箱或手机号登录功能?

在Django Rest Framework中实现邮箱/手机号二选一登录

1. 自定义认证后端

创建authentication.py文件,重写认证逻辑,自动识别用户输入的是邮箱还是手机号:

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
import re

User = get_user_model()

class EmailOrPhoneBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        # 接收用户输入的登录凭证(邮箱/手机号)
        login_credential = username
        if not login_credential or not password:
            return None
        
        # 匹配邮箱格式
        if re.match(r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$', login_credential):
            try:
                user = User.objects.get(email=login_credential)
            except User.DoesNotExist:
                return None
        # 匹配国内手机号格式(可根据需求调整正则)
        elif re.match(r'^1[3-9]\d{9}$', login_credential):
            try:
                user = User.objects.get(phone=login_credential)
            except User.DoesNotExist:
                return None
        else:
            # 格式不匹配,返回None
            return None
        
        # 验证密码并返回用户
        if user.check_password(password) and self.user_can_authenticate(user):
            return user
        return None

2. 配置认证后端

在项目settings.py中添加自定义认证后端:

AUTHENTICATION_BACKENDS = [
    'your_app.authentication.EmailOrPhoneBackend',  # 替换为你的app路径
    'django.contrib.auth.backends.ModelBackend',  # 保留默认后端(可选)
]

3. 自定义登录序列化器

创建serializers.py,定义接收邮箱/手机号的登录序列化器:

from rest_framework import serializers
from django.contrib.auth import authenticate

class LoginSerializer(serializers.Serializer):
    login_credential = serializers.CharField(required=True)
    password = serializers.CharField(required=True, write_only=True)

    def validate(self, attrs):
        login_credential = attrs.get('login_credential')
        password = attrs.get('password')

        # 调用自定义认证后端验证用户
        user = authenticate(request=self.context.get('request'), username=login_credential, password=password)

        if not user:
            raise serializers.ValidationError('邮箱/手机号或密码错误')
        
        attrs['user'] = user
        return attrs

4. 编写登录视图

根据项目使用的认证方式(DRF Token或JWT)编写登录视图:

方式一:DRF Token认证

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 .serializers import LoginSerializer

class LoginView(APIView):
    def post(self, request):
        serializer = LoginSerializer(data=request.data, context={'request': request})
        if serializer.is_valid():
            user = serializer.validated_data['user']
            token, _ = Token.objects.get_or_create(user=user)
            return Response({
                'token': token.key,
                'user_id': user.pk,
                'email': user.email
            }, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

方式二:JWT认证(基于djangorestframework-simplejwt)

from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from django.contrib.auth import authenticate
from rest_framework import serializers

class JWTLoginSerializer(TokenObtainPairSerializer):
    login_credential = serializers.CharField(required=True)
    password = serializers.CharField(required=True, write_only=True)

    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)
        token['email'] = user.email
        return token

    def validate(self, attrs):
        login_credential = attrs.pop('login_credential')
        password = attrs.pop('password')
        
        user = authenticate(request=self.context.get('request'), username=login_credential, password=password)
        if not user:
            raise serializers.ValidationError('邮箱/手机号或密码错误')
        
        # 调用JWT原生逻辑生成token
        data = super().validate({'username': login_credential, 'password': password})
        return data

class CustomTokenObtainPairView(TokenObtainPairView):
    serializer_class = JWTLoginSerializer

5. 配置路由

在项目urls.py中添加登录路由:

from django.urls import path
from .views import LoginView  # 或CustomTokenObtainPairView

urlpatterns = [
    path('login/', LoginView.as_view(), name='login'),
    # JWT方式替换为:
    # path('login/', CustomTokenObtainPairView.as_view(), name='login'),
]

注意事项

  • 确保用户模型包含email和phone字段,且phone字段设置unique=True;若使用默认Django用户模型,需扩展或自定义用户模型。
  • 手机号正则可根据业务需求调整(如支持国际号码)。
  • 如需大小写不敏感匹配邮箱,可将查询改为User.objects.get(email__iexact=login_credential)。

内容的提问来源于stack exchange,提问作者mr_fahad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 23:10:44