如何在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
相关产品推荐
相关产品推荐

