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

Django通过DRF发送重置链接后密码重置表单不显示问题

Django DRF密码重置链接缺失表单问题

问题描述

通过Django标准网站流程发送的密码重置链接,用户点击后能正常显示密码重置表单;但通过DRF API(Flutter应用触发)发送的链接,打开页面后仅显示「Reset Password」按钮,密码输入表单完全缺失。移除自定义CustomPasswordResetConfirmView后问题仍存在,确认是DRF默认密码重置流程导致的异常。


相关代码与配置

  1. 原自定义CustomPasswordResetConfirmView代码(已移除,问题仍存在)
class CustomPasswordResetConfirmView(PasswordResetConfirmView):
    form_class = SetPasswordForm
    success_url = reverse_lazy('users:password_reset_complete')
    template_name = 'users/password_reset_confirm.html'

    @method_decorator(sensitive_post_parameters('new_password1', 'new_password2'))
    @method_decorator(csrf_protect)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['form'] = self.form_class(user=self.request.user)
        return context
  1. 密码重置模板users/password_reset_confirm.html
<main class="mt-5" >
    <div class="container dark-grey-text mt-5">
        <div class="content-section">
            <form method="POST">
                {% csrf_token %}
                <fieldset class="form-group">
                    <legend class="border-bottom mb-4">Reset Password</legend>
                    {{ form|crispy }}
                </fieldset>
                <div class="form-group">
                    <button class="btn btn-outline-info" type="submit">Reset Password</button>
                </div>
            </form>
        </div>
    </div>
</main>
  1. 两种流程的重置链接差异
  • 网站流程链接:https://www.[Domain].com/password-reset-confirm/NTM/bxogt6-68772***************f41836/
  • DRF流程链接:https://www.[Domain].com/password-reset-confirm/1h/bxogub-7eebb4**************deaa0e/
  1. settings.py相关配置
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication'
    ]
}
AUTH_USER_MODEL = 'auth.User'
REST_AUTH_SERIALIZERS = {
    'PASSWORD_RESET_SERIALIZER':
        'api.serializers.PasswordResetSerializer',
}
AUTHENTICATION_BACKENDS=['users.models.EmailBackend']
  1. 未使用的password_reset_email.html模板
{% autoescape off %}
You're receiving this e-mail because you requested a password reset for your user account at {{ site_name }}.

Please go to the following page and choose a new password:
{% block reset_link %}
{{ protocol }}://{{ domain }}/api/{% url 'password_reset_confirm' uidb64=uid token=token %}
{% endblock %}

Your username, in case you've forgotten: {{ user.username }}

Thanks for using our site!

The {{ site_name }} team.

{% endautoescape %}

问题根源分析

DRF默认密码重置流程生成的uidb64格式与Django原生流程不兼容:

  • Django原生采用整数用户ID的base64编码(示例中的NTM对应十进制53)
  • DRF流程生成的是字符串/UUID格式的base64编码(示例中的1h)

Django原生的PasswordResetConfirmView仅能正确解析整数ID的编码,当传入字符串/UUID格式的uidb64时,视图无法获取有效用户,导致表单未初始化,模板中{{ form|crispy }}渲染为空,最终只显示按钮。此外,自定义PasswordResetSerializer若未对齐原生逻辑,也会加剧该问题。


解决方案

方案1:统一DRF与原生的UID生成逻辑

修改自定义PasswordResetSerializer,采用Django原生方式生成uidb64和token:

# api/serializers.py
from django.contrib.auth.tokens import default_token_generator
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.core.mail import send_mail
from django.conf import settings
from rest_framework import serializers
from django.contrib.auth import get_user_model

User = get_user_model()

class PasswordResetSerializer(serializers.Serializer):
    email = serializers.EmailField()

    def validate_email(self, value):
        self.user = User.objects.filter(email=value).first()
        if not self.user:
            raise serializers.ValidationError("用户不存在")
        return value

    def save(self):
        # 原生方式生成uidb64和token
        uid = urlsafe_base64_encode(force_bytes(self.user.pk))
        token = default_token_generator.make_token(self.user)
        
        # 构建指向网站重置视图的链接
        reset_url = f"{settings.FRONTEND_DOMAIN}/password-reset-confirm/{uid}/{token}/"
        
        # 发送重置邮件
        subject = "重置你的密码"
        message = f"请点击以下链接重置密码:{reset_url}"
        send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [self.user.email])

方案2:修改视图兼容UUID/字符串格式UID

若用户模型使用UUID作为主键,修改PasswordResetConfirmView以支持多格式UID解析:

from django.utils.encoding import force_str, force_bytes
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth.views import PasswordResetConfirmView
from django.contrib.auth import get_user_model
from django.shortcuts import redirect

User = get_user_model()

class CustomPasswordResetConfirmView(PasswordResetConfirmView):
    form_class = SetPasswordForm
    success_url = reverse_lazy('users:password_reset_complete')
    template_name = 'users/password_reset_confirm.html'

    def dispatch(self, request, *args, **kwargs):
        self.user = None
        # 先尝试解析UUID格式UID
        try:
            uid = force_str(urlsafe_base64_decode(kwargs['uidb64']))
            self.user = User.objects.get(pk=uid)
        except (TypeError, ValueError, OverflowError, User.DoesNotExist):
            # 回退到整数ID解析
            try:
                uid = force_str(urlsafe_base64_decode(kwargs['uidb64']))
                self.user = User.objects.get(pk=int(uid))
            except (TypeError, ValueError, OverflowError, User.DoesNotExist):
                pass
        
        if self.user is None or not default_token_generator.check_token(self.user, kwargs['token']):
            return redirect('password_reset_invalid')
        
        return super().dispatch(request, *args, **kwargs)

方案3:修正邮件模板链接路径

确保DRF发送的重置链接指向网站视图而非API路径,若使用dj-rest-auth可在settings中配置:

# settings.py
DJ_REST_AUTH = {
    'PASSWORD_RESET_CONFIRM_URL': 'password-reset-confirm/{uidb64}/{token}/',
}

同时修改邮件模板,移除/api/前缀:

{% block reset_link %}
{{ protocol }}://{{ domain }}/{% url 'password_reset_confirm' uidb64=uid token=token %}
{% endblock %}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 00:01:00