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

Django 3.1 auth模块:如何验证重置密码Token并更新用户密码?

Django 3.1 纯API应用密码重置后续流程问题

已实现的密码重置发起视图

基于Django 3.1的auth模块开发纯API应用,已实现发起密码重置的视图:

class ResetPasswordView(SuccessMessageMixin, PasswordResetView):
    reset_password_template_name = 'templates/users/password_reset.html'
    email_template_name = 'users/password_reset_email.html'
    subject_template_name = 'users/password_reset_subject'
    success_message = "We've emailed you instructions for setting your password, " \
                      "if an account exists with the email you entered. You should receive them shortly." \
                      " If you don't receive an email, " \
                      "please make sure you've entered the address you registered with, and check your spam folder."
    success_url = reverse_lazy('users-home')

    @method_decorator(csrf_exempt)
    def dispatch(self, request, *args, **kwargs):
        request.csrf_processing_done = True
        return super().dispatch(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        email = json.loads(request.body).get('username')
        try:
            if User.objects.get(email=email).is_active:
                form = PasswordResetForm({'email': email})
                print("form valid? %s" % form.is_valid())
                if form.is_valid():
                    request = HttpRequest()
                    request.META['SERVER_NAME'] = socket.gethostbyname('localhost') #'127.0.0.1'
                    request.META['SERVER_PORT'] = 8000
                    # calling save() sends the email
                    # check the form in the source code for the signature and defaults
                    form.save(request=request,
                        use_https=False,
                        from_email="laredotornado@yahoo.com",
                        email_template_name='../templates/users/password_reset_email.html')
                print("email: %s " % email)
                return super(ResetPasswordView, self).post(request, *args, **kwargs)
        except Exception as e:
            print("\n\nerror ...\n\n")
            print(e)
            # this for if the email is not in the db of the system
            return super(ResetPasswordView, self).post(request, *args, **kwargs)

生成的重置链接示例

视图发送的邮件中包含如下格式的重置链接:

http://127.0.0.1:8000/password-reset-confirm/Mg/bhd3nc-29fa9003c9c61c2bda5cff0a66b38bdf/

问题

如何将链接中的Token与用户设置的新密码提交至服务器,完成Token验证并更新用户密码?

补充的错误栈追踪

尝试过程中出现如下错误:

Traceback (most recent call last):
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.9/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.9/site-packages/django/utils/decorators.py", line 43, in _wrapper
    return bound_method(*args, **kwargs)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/directory/views.py", line 395, in dispatch
    return super().dispatch(request, **data)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.9/site-packages/django/utils/decorators.py", line 43, in _wrapper
    return bound_method(*args, **kwargs)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.9/site-packages/django/views/decorators/debug.py", line 89, in sensitive_post_parameters_wrapper
    return view(request, *args, **kwargs)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.9/site-packages/django/utils/decorators.py", line 43, in _wrapper
    return bound_method(*args, **kwargs)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.9/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.9/site-packages/django/contrib/auth/views.py", line 260, in dispatch
    assert 'uidb64' in kwargs and 'token' in kwargs

异常类型: AssertionError at /password-reset-complete

解决方案

1. 实现密码重置确认API视图

继承PasswordResetConfirmView,适配API场景,处理JSON请求并完成Token验证、密码更新:

from django.contrib.auth.views import PasswordResetConfirmView
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
from django.contrib.auth.tokens import default_token_generator
from django.utils.http import urlsafe_base64_decode
from django.contrib.auth.models import User
import json

@method_decorator(csrf_exempt, name='dispatch')
class ResetPasswordConfirmAPIView(PasswordResetConfirmView):
    def dispatch(self, request, *args, **kwargs):
        request.csrf_processing_done = True
        return super().dispatch(request, *args, **kwargs)

    def post(self, request, uidb64, token, *args, **kwargs):
        try:
            # 解析请求体中的新密码
            data = json.loads(request.body)
            new_password = data.get('new_password')
            confirm_password = data.get('confirm_password')

            # 校验密码一致性
            if not new_password or new_password != confirm_password:
                return JsonResponse({'status': 'error', 'message': '两次密码输入不一致'}, status=400)

            # 解码用户ID并获取用户
            uid = urlsafe_base64_decode(uidb64).decode()
            user = User.objects.get(pk=uid)

            # 验证重置Token有效性
            if not default_token_generator.check_token(user, token):
                return JsonResponse({'status': 'error', 'message': '重置链接无效或已过期'}, status=400)

            # 更新用户密码
            user.set_password(new_password)
            user.save()

            return JsonResponse({'status': 'success', 'message': '密码重置成功'})
        except User.DoesNotExist:
            return JsonResponse({'status': 'error', 'message': '用户不存在'}, status=404)
        except json.JSONDecodeError:
            return JsonResponse({'status': 'error', 'message': '请求格式错误,需提交JSON数据'}, status=400)
        except Exception as e:
            return JsonResponse({'status': 'error', 'message': str(e)}, status=500)

2. 配置正确的URL路由

确保重置确认视图的URL包含uidb64和token参数,避免路由错误:

from django.urls import path
from .views import ResetPasswordView, ResetPasswordConfirmAPIView

urlpatterns = [
    # 发起密码重置
    path('password-reset/', ResetPasswordView.as_view(), name='password_reset'),
    # 密码重置确认API
    path('password-reset-confirm/<uidb64>/<token>/', ResetPasswordConfirmAPIView.as_view(), name='password_reset_confirm'),
    # 纯API应用可移除password-reset-complete路由,无需前端跳转页面
]

3. 前端提交请求示例

前端通过POST请求提交新密码,请求URL为邮件中的重置链接:

// 示例:使用fetch提交请求
const resetUrl = 'http://127.0.0.1:8000/password-reset-confirm/Mg/bhd3nc-29fa9003c9c61c2bda5cff0a66b38bdf/';
const newPassword = 'your_secure_new_password';

fetch(resetUrl, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({
        new_password: newPassword,
        confirm_password: newPassword
    })
})
.then(response => response.json())
.then(result => {
    if (result.status === 'success') {
        console.log('密码重置成功');
    } else {
        console.error('重置失败:', result.message);
    }
})
.catch(error => console.error('请求出错:', error));

解决AssertionError错误

错误原因是/password-reset-complete路由绑定了需要uidb64和token参数的视图,或路由配置错误。处理方式:

  • 确保password-reset-confirm路由包含<uidb64>和<token>参数,且对应到正确的确认视图
  • 纯API应用无需password-reset-complete页面,直接移除该路由即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 04:45:57