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

如何在Django自定义密码重置系统中通过验证码完成用户身份校验

自定义密码重置系统验证码校验实现

现有代码回顾

models.py

class UserProfile(models.Model):
  user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE,related_name="userprofile")
  forget_password_token = models.CharField(max_length=100,blank=True,null=True)
  # 后续优化可加字段:forget_password_token_expire = models.DateTimeField(blank=True,null=True) 存储验证码过期时间

发送验证码视图(现有逻辑)

def ForgetPasswordSendCode(request):
    if request.method == "POST":
       email = request.POST["email"]
       User = get_user_model()
       if not User.objects.filter(email=email).first():
           messages.success(request, "Invalid mail")
           return redirect('members:reset-password') 
       user_obj = User.objects.get(email=email)
       
       reset_code = str(rand_number_mail()) #生成随机验证码
      
       profile_obj = UserProfile.objects.get(user=user_obj)
       profile_obj.forget_password_token = reset_code 
       # 后续优化可加:profile_obj.forget_password_token_expire = timezone.now() + timedelta(minutes=15) 设置15分钟有效期
       profile_obj.save()

       current_site = get_current_site(request)
       subject = 'Verification Code'
       context = {
                    'user_first_name':  user_obj.first_name ,
                    'user_last_name':  user_obj.last_name ,
                    'domain': current_site.domain,
                    'reset_code': reset_code
                }
       html_body = render_to_string('mail/resetpassword-mail.html', context)
       to_email = request.POST["email"] 
       email = EmailMultiAlternatives(subject=subject,from_email='noreply@farhyn.com',to=[to_email]) 
       email.attach_alternative(html_body, "text/html") 
       email.send(fail_silently=False)
       # 可选优化:将邮箱存入session,改密页无需用户重复输入邮箱
       request.session['reset_pwd_email'] = to_email
       messages.success(request, "An password reset code sent to your email")
       return redirect('members:change-password')   #提交邮箱后跳转到密码重置页
    return render(request, 'members/password_reset_form.html')

密码重置校验视图实现

from django.contrib.auth.hashers import make_password

def ChangePassWordPage(request):
    if request.method == "POST":
        # 1. 获取提交参数,如果之前将邮箱存入session可以直接取,不用让用户重复提交邮箱
        # email = request.session.get('reset_pwd_email')
        email = request.POST.get('email')
        input_code = request.POST.get('reset_code')
        new_pwd = request.POST.get('new_password')
        confirm_pwd = request.POST.get('confirm_password')

        # 2. 基础参数校验
        if not all([email, input_code, new_pwd, confirm_pwd]):
            messages.error(request, "所有字段都不能为空")
            return render(request,'members/password_change.html')
        if new_pwd != confirm_pwd:
            messages.error(request, "两次输入的密码不一致")
            return render(request,'members/password_change.html')

        User = get_user_model()
        # 3. 校验用户是否存在
        try:
            user_obj = User.objects.get(email=email)
            profile_obj = UserProfile.objects.get(user=user_obj)
        except (User.DoesNotExist, UserProfile.DoesNotExist):
            messages.error(request, "用户不存在")
            return render(request,'members/password_change.html')

        # 4. 校验验证码是否正确
        if profile_obj.forget_password_token != input_code:
            messages.error(request, "验证码错误")
            return render(request,'members/password_change.html')
        
        # 如果加了过期时间,可新增这步校验(需要提前导入django.utils.timezone)
        # if timezone.now() > profile_obj.forget_password_token_expire:
        #     messages.error(request, "验证码已过期,请重新发送")
        #     return render(request,'members/password_change.html')

        # 5. 修改密码
        user_obj.password = make_password(new_pwd)
        user_obj.save()

        # 6. 清空验证码和session,防止重复使用
        profile_obj.forget_password_token = None
        # profile_obj.forget_password_token_expire = None
        profile_obj.save()
        if 'reset_pwd_email' in request.session:
            del request.session['reset_pwd_email']

        messages.success(request, "密码修改成功,请重新登录")
        return redirect('members:login')

    return render(request,'members/password_change.html')

前端表单要求

members/password_change.html的表单需要包含以下字段:

  • 邮箱输入框(如果没将邮箱存入session的话),name属性为email
  • 验证码输入框,name属性为reset_code
  • 新密码输入框,name属性为new_password
  • 确认密码输入框,name属性为confirm_password
  • 提交按钮,请求方式为POST

优化建议

  • 给验证码增加15-30分钟的过期时间,避免验证码长期有效带来的安全风险
  • 验证码校验通过后立刻清空存储的token,防止同一个验证码被多次使用
  • 增加新密码强度校验,限制密码长度、字符类型等,避免弱密码
  • 对发送验证码、校验验证码的接口增加频率限制,防止暴力破解和恶意请求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 10:09:03