难点:
解决方案:
settings.py 中正确配置邮件发送相关设置,如 EMAIL_BACKEND、EMAIL_HOST、EMAIL_PORT、EMAIL_USE_TLS、EMAIL_HOST_USER、EMAIL_HOST_PASSWORD 等。难点:
解决方案:
django-ratelimit 库),防止滥用。难点:
解决方案:
default_token_generator,它已经实现了安全的令牌生成和验证机制。难点:
解决方案:
urlsafe_base64_encode 和 urlsafe_base64_decode 进行安全的编码和解码。难点:
解决方案:
难点:
解决方案:
{% csrf_token %}。SetPasswordForm,它会对新密码进行强度验证。尽管实现忘记密码功能涉及多个技术难点,但通过 Django 提供的内置功能和第三方库,可以较为顺利地解决这些问题。关键在于确保每一步的安全性和用户体验,避免潜在的安全漏洞和用户困扰。
用户在登录页面点击“忘记密码”链接,进入请求密码重置页面。
用户在请求密码重置页面输入注册时使用的邮箱地址并提交表单。
系统接收到表单提交请求,检查数据库中是否存在该邮箱地址对应的用户。
如果邮箱地址有效,系统生成密码重置令牌和UID,用于唯一标识用户和验证请求的有效性。
系统通过配置好的邮件服务器,向用户发送包含密码重置链接的电子邮件。链接中包含UID和令牌。
用户收到电子邮件,点击邮件中的重置链接,进入密码重置页面。
系统接收到用户点击链接的请求,验证链接中的UID和令牌是否有效,如果无效则提示错误信息。
用户在密码重置页面输入新密码并提交表单。
系统接收到表单提交请求,验证新密码的有效性,并更新数据库中用户的密码。
密码更新成功后,系统提示用户密码已重置成功,用户可以使用新密码登录。
+--------------------------+
| 用户请求密码重置 |
+--------------------------+
|
v
+--------------------------+
| 用户填写邮箱并提交表单 |
+--------------------------+
|
v
+--------------------------+
| 验证邮箱是否存在于数据库 |
+--------------------------+
|
v
+--------------------------+
| 生成密码重置令牌和UID |
+--------------------------+
|
v
+--------------------------+
| 发送包含重置链接的电子邮件 |
+--------------------------+
|
v
+--------------------------+
| 用户点击重置链接,进入重置页面 |
+--------------------------+
|
v
+--------------------------+
| 验证令牌和UID是否有效 |
+--------------------------+
|
v
+--------------------------+
| 用户填写新密码并提交表单 |
+--------------------------+
|
v
+--------------------------+
| 验证新密码并更新用户密码 |
+--------------------------+
|
v
+--------------------------+
| 密码重置成功,提示用户登录 |
+--------------------------+
首先,确保你在 settings.py 中配置了邮件发送功能,以QQ为例,根据这个文档设置smtp:
# Email settings
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.qq.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'your-email@example.com'
EMAIL_HOST_PASSWORD = '授权码'
DEFAULT_FROM_EMAIL = 'your-email@example.com'
确保你的email开通了SMTP功能,获得授权码,如下:


成功开启SMTP服务后,邮件设置页面如下:

创建一个表单用于输入用户的邮箱地址:
from django import forms
class PasswordResetRequestForm(forms.Form):
email = forms.EmailField(label="Enter your email", max_length=254)
创建两个视图,一个用于请求密码重置链接,另一个用于实际重置密码。
from django.contrib.auth.tokens import default_token_generator
from django.contrib.auth.models import User
from django.template.loader import render_to_string
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.utils.encoding import force_bytes
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import send_mail
from django.shortcuts import render, redirect
from django.urls import reverse
from .forms import PasswordResetRequestForm
from django.contrib.auth.forms import SetPasswordForm
class PasswordResetRequestView(View):
def get(self, request):
form = PasswordResetRequestForm()
return render(request, 'password_reset_request.html', {'form': form})
def post(self, request):
form = PasswordResetRequestForm(request.POST)
if form.is_valid():
email = form.cleaned_data['email']
user = User.objects.filter(email=email).first()
if user:
token = default_token_generator.make_token(user)
uid = urlsafe_base64_encode(force_bytes(user.pk))
site = get_current_site(request)
link = request.build_absolute_uri(
reverse('password_reset_confirm', kwargs={'uidb64': uid, 'token': token})
)
mail_subject = 'Reset your password'
message = render_to_string('password_reset_email.html', {
'user': user,
'domain': site.domain,
'uid': uid,
'token': token,
'link': link,
})
send_mail(mail_subject, message, 'your-email@example.com', [email])
return redirect('password_reset_done')
return render(request, 'password_reset_request.html', {'form': form})
class PasswordResetConfirmView(View):
def get(self, request, uidb64=None, token=None):
uid = urlsafe_base64_decode(uidb64).decode()
user = User.objects.get(pk=uid)
if default_token_generator.check_token(user, token):
form = SetPasswordForm(user)
return render(request, 'password_reset_confirm.html', {'form': form, 'user': user})
else:
return HttpResponse('Token is invalid!')
def post(self, request, uidb64=None, token=None):
uid = urlsafe_base64_decode(uidb64).decode()
user = User.objects.get(pk=uid)
form = SetPasswordForm(user, request.POST)
if form.is_valid():
form.save()
return redirect('password_reset_complete')
return render(request, 'password_reset_confirm.html', {'form': form})
password_reset_request.html{% load static %}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>请求密码重置title>
<link rel="stylesheet" href="{% static 'blog/css/post_list.css' %}">
head>
<body>
<h2>请求密码重置h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">发送密码重置链接button>
form>
body>
html>
password_reset_email.html<p>Hi {{ user.username }},p>
<p>点击下面的链接重置你的密码:p>
<p><a href="{{ link }}">{{ link }}a>p>
password_reset_confirm.html{% load static %}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>重置密码title>
<link rel="stylesheet" href="{% static 'blog/css/post_list.css' %}">
head>
<body>
<h2>重置密码h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">重置密码button>
form>
body>
html>
在 urls.py 文件中添加相应的 URL 路径:
from django.urls import path
from .views import PasswordResetRequestView, PasswordResetConfirmView
urlpatterns = [
path('password_reset/', PasswordResetRequestView.as_view(), name='password_reset_request'),
path('password_reset/done/', TemplateView.as_view(template_name='password_reset_done.html'), name='password_reset_done'),
path('reset///' , PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
path('reset/done/', TemplateView.as_view(template_name='password_reset_complete.html'), name='password_reset_complete'),
]
运行开发服务器:
python manage.py runserver
访问密码重置页面:
打开浏览器,访问 http://127.0.0.1:8000/password_reset/ 并测试密码重置功能。
通过这些步骤,你应该能够成功地在 Django 项目中实现忘记密码功能,包括请求密码重置链接、发送重置邮件、以及重置密码。

登录页面点击忘记密码。进入请求密码重置页面,如下:

输入email,发动密码重置链接到邮箱地址。发送成功,如下:

打开邮件箱能看到重置密码的邮件如下:

点击链接跳转到重置密码页面,如下:

如果长时间后点击操作,跳转到重置页面会报错如下:

重置密码成功,如下:
