使用 DRF 时不显示 Django 密码重置表单,可在网站上工作

Django Password Reset Form Not Showing When Using DRF, Works from Website

提问人:A_K 提问时间:11/16/2023 更新时间:11/16/2023 访问量:19

问:

我的 Django 应用程序遇到了涉及密码重置功能的问题。当密码重置链接通过标准的 Django 网站流程发送时,一切都按预期工作:用户会收到一封带有链接的电子邮件,单击该链接后,他们将被带到一个包含重置密码表单的页面。

但是,当重置链接通过 Django Rest Framework (DRF) API(从 Flutter 应用程序触发)发送时,用户仍然会收到带有链接的电子邮件,但打开的页面不会显示密码重置表单。页面上的其余 HTML 内容正确呈现,但表单本身丢失。

这是我在 Django 中 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

模板 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>

我不确定要寻找什么,要么是发送给用户重置的链接不正确,因为当从网站收到电子邮件时显示链接如下:

Subject: [Displayname] Password Reset E-mail
Hello from [Displayname]!

You're receiving this e-mail because you or someone else has requested a password for your user account.
It can be safely ignored if you did not request a password reset. Click the link below to reset your password.

https://www.[Domain].com/password-reset-confirm/1h/bxogub-7eebb4**************deaa0e/

In case you forgot, your username is Username.

Thank you for using [Displayname]!
www.[Domain].com

而从 DRF API flutter 看起来像这样:

Subject: Password reset on [Displayname]
You're receiving this email because you requested a password reset for your user account at [Displayname].

Please go to the following page and choose a new password:

https://www.[Domain].com/password-reset-confirm/NTM/bxogt6-68772***************f41836/

Your username, in case you’ve forgotten: Username

Thanks for using our site!

The [Displayname]team
django django-rest-framework django-forms

评论


答:

0赞 Sujata R 11/16/2023 #1

听起来 Django 站点(URL 中的 1h)和 DRF API (NTM) 之间的链接格式不匹配导致验证失败。

DRF 重置令牌可能与 Django 的预期格式不兼容。

最好的做法是像 Django 一样生成令牌,直接在 DRF 视图中使用 PasswordResetTokenGenerator。

这样,链接格式将匹配,避免当用户单击密码重置 URL 时 Django 端出现任何验证错误。

from django.contrib.auth.tokens import PasswordResetTokenGenerator
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode

class CustomPasswordResetConfirmView(PasswordResetConfirmView):
    # Your existing code...

    def get_reset_token(self, user):
        token_generator = PasswordResetTokenGenerator()
        uidb64 = urlsafe_base64_encode(force_bytes(user.pk))
        token = token_generator.make_token(user)
        return f"{uidb64}/{token}"

    def send_reset_email(self, user):
        reset_token = self.get_reset_token(user)
        reset_link = f"https://www.[Domain].com/password-reset-confirm/{reset_token}/"
        # Send the email with the correct reset link
        # ...

    # Your existing code...

需要检查的一些关键事项:

  • 令牌生成应与 Django 的 PasswordResetTokenGenerator 完全匹配。

  • 记录令牌以确保其与预期格式匹配。

  • 确认 urls.py 中的重置 URL 结构与 API 提供的内容匹配。

  • 在 PasswordResetConfirmView 上记录视图参数可能会显示值不匹配。

该问题似乎源于 DRF 和 Django 身份验证之间的令牌格式不一致。确保令牌的生成方式相同,每个 PasswordResetTokenGenerator,并且 URL 模式正确排列应该可以解决它。

在整个过程中进行日志记录将有助于查明发生任何错误或不匹配的位置。通过一些调试,我们可以使此密码重置流程在 API 和站点中无缝运行。

0赞 aijogja 12/1/2023 #2

您是否安装了任何与密码重置/身份验证相关的库/包?因为我认为这是图书馆的电子邮件模板。

对我来说,我认为如果我使用库来处理这个问题会更简单。例如,我通常使用 dj-rest-auth 来处理与身份验证、注册、忘记密码和重置密码相关的操作。

对于带有 Flutter 的 DRF。

流程应为:

  1. Flutter 命中端点重置密码
  2. Django 向用户发送电子邮件
  3. Flutter 句柄深层链接,用于在 Flutter 应用内创建新密码
  4. flutter hit endpoint password reset 使用参数进行确认,并且uidtokennew password