如何基于graphene-django实现密码重置邮件发送?
Django 内置的 django.contrib.auth 模块本身就提供了密码重置全流程的可直接调用组件,完全不需要依赖第三方包,你可以直接在 Graphene 的 Mutation 中封装调用,和你之前迁移登录时用的authenticate、login方法一样稳定,和原有认证体系完全兼容。
核心依赖的内置组件
你不需要自己实现令牌生成、邮件发送、安全校验这些逻辑,直接复用Django官方维护的成熟实现即可:
django.contrib.auth.tokens.PasswordResetTokenGenerator:内置的密码重置令牌生成/校验器,生成的令牌单次有效、带过期机制,和Django原生模板视图用的令牌逻辑完全一致,不存在安全兼容问题。django.contrib.auth.forms.PasswordResetForm:封装了完整的邮箱校验、重置邮件发送逻辑,自动适配你settings里的邮件配置,不会因为传入不存在的邮箱抛出异常,天然防用户邮箱枚举。django.contrib.auth.forms.SetPasswordForm:封装了密码强度校验、密码更新逻辑,和你后台改密码的校验规则完全统一。
具体实现步骤
1. 基础配置
首先在settings.py里补充必要配置,和你之前Django模板版本的配置保持一致即可:
# 邮件配置沿用你原有项目的配置即可 EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" DEFAULT_FROM_EMAIL = "noreply@your-project-domain.com" # 配置React端的密码重置页面地址,邮件里的链接会直接跳转到前端页面 FRONTEND_RESET_PASSWORD_PATH = "https://your-frontend-domain/reset-password"
2. 实现发送重置邮件的Mutation
import graphene from django.conf import settings from django.contrib.auth.forms import PasswordResetForm class SendPasswordResetEmail(graphene.Mutation): success = graphene.Boolean(required=True) class Arguments: email = graphene.String(required=True) def mutate(self, info, email): form = PasswordResetForm(data={"email": email}) if form.is_valid(): request = info.context form.save( request=request, use_https=request.is_secure(), from_email=settings.DEFAULT_FROM_EMAIL, # 如果你要自定义邮件模板,替换成自己的模板路径即可 email_template_name="registration/password_reset_email.html", extra_email_context={ "frontend_reset_path": settings.FRONTEND_RESET_PASSWORD_PATH } ) # 无论邮箱是否存在都返回成功,避免攻击者枚举系统内的注册邮箱 return SendPasswordResetEmail(success=True)
3. 实现提交新密码的Mutation
用户点击邮件里的前端链接,跳转到React重置页面后,前端拿到url里的uid和token参数,和用户输入的新密码一起传给这个接口即可:
import graphene from django.contrib.auth import get_user_model from django.contrib.auth.forms import SetPasswordForm from django.contrib.auth.tokens import default_token_generator from django.utils.http import urlsafe_base64_decode User = get_user_model() class ResetPassword(graphene.Mutation): success = graphene.Boolean(required=True) error_msg = graphene.String() class Arguments: uidb64 = graphene.String(required=True) token = graphene.String(required=True) new_password = graphene.String(required=True) def mutate(self, info, uidb64, token, new_password): user = None try: uid = urlsafe_base64_decode(uidb64).decode() user = User.objects.get(pk=uid) except (TypeError, ValueError, OverflowError, User.DoesNotExist): pass # 直接调用内置方法校验令牌有效性、是否过期 if user and default_token_generator.check_token(user, token): form = SetPasswordForm( user=user, data={"new_password1": new_password, "new_password2": new_password} ) if form.is_valid(): form.save() return ResetPassword(success=True, error_msg=None) return ResetPassword(success=False, error_msg=form.errors.as_text().split("\n")[0]) return ResetPassword(success=False, error_msg="重置链接无效或已过期,请重新申请重置邮件")
注意事项
- 所有逻辑完全复用Django内置auth的官方实现,没有额外的依赖维护成本,稳定性和你已经上线的登录逻辑一致。
- 如果需要自定义邮件内容,只需要自己写邮件模板替换默认模板即可,模板中可以直接拿到
uid、token、frontend_reset_path变量,拼接成前端跳转链接即可,示例链接格式:{{ frontend_reset_path }}?uid={{ uid }}&token={{ token }}。 - 这套实现不需要修改你现有项目的任何认证配置,迁移成本极低。
内容的提问来源于stack exchange,提问作者NICOLÁS ANTONIO PINO LEVA
相关产品推荐
相关产品推荐

