Djoser框架下邮箱修改验证问题及重置流程功能扩展需求
Djoser框架下邮箱修改验证问题及重置流程功能扩展需求
兄弟,我完全懂你现在的困扰——在Djoser框架里修改用户邮箱时,没法完成有效的验证闭环,而且你想要一套更严谨的邮箱重置+激活机制:用户先提交注册邮箱申请重置,收到带uid和token的链接后,提交新邮箱,此时账号被设为未激活状态,必须通过新邮箱收到的激活链接验证后才能恢复使用。下面我帮你一步步实现这个需求:
一、先梳理现有代码的问题
你当前的配置里开启了USERNAME_CHANGED_EMAIL_CONFIRMATION,但Djoser默认的邮箱修改流程只是发送确认邮件,不会禁用账号,也不会强制新邮箱激活。所以我们需要自定义两个核心端点:reset_email/(申请重置邮箱)和reset_email_confirm/(确认新邮箱并触发激活)。
二、扩展序列化器(serializers.py)
先写两个序列化器,分别处理重置请求和确认请求的参数校验:
from rest_framework import serializers from djoser.serializers import UidAndTokenSerializer from .models import User from django.contrib.auth.tokens import default_token_generator from django.utils.http import urlsafe_base64_decode from django.utils.encoding import force_str # 处理重置邮箱的请求(校验邮箱是否存在) class ResetEmailSerializer(serializers.Serializer): email = serializers.EmailField() def validate_email(self, value): try: # 排除软删除的用户 User.objects.get(email=value, soft_deleted=False) except User.DoesNotExist: raise serializers.ValidationError("该邮箱未注册或已被软删除") return value # 处理重置确认的请求(校验uid、token,以及新邮箱是否可用) class ResetEmailConfirmSerializer(UidAndTokenSerializer): new_email = serializers.EmailField() def validate(self, attrs): # 调用Djoser自带的方法验证uid和token self.user = self.get_user(attrs["uid"]) if not default_token_generator.check_token(self.user, attrs["token"]): raise serializers.ValidationError("无效的验证链接,请检查链接是否过期或正确") # 检查新邮箱是否已被其他用户使用 if User.objects.filter(email=attrs["new_email"]).exists(): raise serializers.ValidationError("该邮箱已被注册,请更换其他邮箱") return attrs
三、自定义视图逻辑(views.py)
接下来写两个视图,处理这两个端点的业务逻辑:
from rest_framework import generics, status from rest_framework.response import Response from djoser.utils import send_activation_email from .serializers import ResetEmailSerializer, ResetEmailConfirmSerializer from .models import User from django.contrib.auth.tokens import default_token_generator from django.utils.http import urlsafe_base64_encode from django.utils.encoding import force_bytes from django.core.mail import send_mail from django.conf import settings # 处理重置邮箱申请的视图 class ResetEmailView(generics.GenericAPIView): serializer_class = ResetEmailSerializer def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) user = User.objects.get(email=serializer.validated_data["email"]) # 生成uid和token(和Djoser激活/重置密码的逻辑一致) uid = urlsafe_base64_encode(force_bytes(user.pk)) token = default_token_generator.make_token(user) # 构建前端的重置确认页面链接(替换成你的实际前端地址) reset_confirm_url = f"{settings.FRONTEND_URL}/auth/reset-email-confirm/{uid}/{token}" # 发送重置邮箱通知邮件 send_mail( subject="【你的平台】重置邮箱地址申请", message=f"你申请重置邮箱地址,请点击下方链接完成操作:\n{reset_confirm_url}\n如果不是你操作,请忽略此邮件", from_email=settings.DEFAULT_FROM_EMAIL, recipient_list=[user.email], fail_silently=False, ) return Response({"detail": "重置邮箱链接已发送至你的注册邮箱,请查收"}, status=status.HTTP_200_OK) # 处理重置邮箱确认的视图 class ResetEmailConfirmView(generics.GenericAPIView): serializer_class = ResetEmailConfirmSerializer def post(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) user = serializer.user new_email = serializer.validated_data["new_email"] # 核心逻辑:更新用户邮箱,将账号设为未激活,然后发送新邮箱激活链接 user.email = new_email user.is_active = False user.save() # 调用Djoser自带的发送激活邮件方法(复用现有激活逻辑) send_activation_email(user, request) return Response({"detail": "新邮箱已提交,激活链接已发送至你的新邮箱,请验证后登录"}, status=status.HTTP_200_OK)
四、添加路由(urls.py)
把自定义的端点加到路由里:
from django.urls import path, include from .views import ResetEmailView, ResetEmailConfirmView urlpatterns = [ # 保留Djoser默认的路由 path('auth/', include('djoser.urls')), # 自定义的重置邮箱端点 path('auth/users/reset_email/', ResetEmailView.as_view(), name='reset_email'), path('auth/users/reset_email_confirm/', ResetEmailConfirmView.as_view(), name='reset_email_confirm'), ]
五、配置与注意事项
- 确保Djoser配置正确:你现有
settings.py里的SEND_ACTIVATION_EMAIL和ACTIVATION_URL已经配置好了,不需要额外修改,激活邮件会自动使用你设置的ACTIVATION_URL。 - 邮件模板优化:如果觉得默认邮件内容太简单,可以自定义Djoser的邮件模板,在
templates/djoser/email/activation.html(或txt格式)里修改内容。 - Token有效期:Djoser默认的token有效期是3天,如果需要调整,可以自定义
default_token_generator。 - 软删除处理:我们在序列化器里已经排除了软删除的用户,符合你的模型设计。
这样一套流程下来,就完全实现了你想要的功能:用户申请重置邮箱→收验证链接→提交新邮箱→账号被禁用→新邮箱收激活链接→激活后账号恢复可用。
备注:内容来源于stack exchange,提问作者Azhar Uddin Sheikh




