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

Django邮箱验证功能问题:未验证邮箱被占用后无法用于新用户注册

Django邮箱验证功能问题:未验证邮箱被占用后无法用于新用户注册

我太懂这个问题的尴尬了——有人手滑或者故意用别人的邮箱注册,结果真实邮箱主人想注册时,却因为数据库里躺着个未激活的“僵尸账号”被卡住,确实是邮箱验证流程里很容易漏掉的细节。咱们来给你的代码加几道补丁,彻底解决这个问题。

问题根源

你的当前逻辑是:只要邮箱在数据库里存在(不管是否激活),Django用户模型的邮箱唯一约束就会触发,导致新注册失败。而恶意/误操作的用户留下的未激活账号,就像占着位置的僵尸,堵着真实主人的路。

解决方案思路

我们可以从两个层面入手:

  1. 注册时拦截处理:当用户用已存在的未激活邮箱注册时,不拒绝,而是重新发送验证邮件给该邮箱,同时提示用户。
  2. 定期清理僵尸账号:自动删除超过一定时间(比如7天)未激活的账号,从根源上减少冲突。

具体代码修改

1. 修改注册视图,处理未激活邮箱的重复注册

打开你的register_page视图,修改POST请求的处理逻辑,在表单验证通过后,先检查是否有同邮箱的未激活用户:

def register_page(request: HttpRequest):
    if request.method == "GET":
        reg_form = UserRegisterForm()
        context = {"form": reg_form}
        return render(request, "users/register.html", context)
    
    if request.method == "POST":
        reg_form = UserRegisterForm(request.POST)
        context = {"form": reg_form}
        
        if reg_form.is_valid():
            email = reg_form.cleaned_data["email"]
            # 检查是否存在未激活的同邮箱用户
            existing_inactive_user = User.objects.filter(email=email, is_active=False).first()
            
            if existing_inactive_user:
                # 重新发送验证邮件给这个未激活的账号
                send_email_validation(request, existing_inactive_user, reg_form)
                # 给用户返回提示信息
                context["message"] = "该邮箱已有未完成验证的账号,我们已重新发送验证邮件至您的邮箱,请查收后完成激活。"
                return render(request, "users/register.html", context)
            
            # 正常创建新用户流程
            user: User = reg_form.save(commit=False)
            user.is_active = False
            user.save()
            send_email_validation(request, user, reg_form)
            return redirect("users_login")
    
    # 表单验证失败,返回带错误的表单
    return render(request, "users/register.html", context)

2. (可选)添加定期清理未激活账号的逻辑

如果想从根源上清理僵尸账号,可以用Django的自定义命令,配合定时任务自动删除过期未激活的账号:

  1. 在你的usersapp下创建management/commands目录(如果没有的话),然后新建clean_inactive_users.py文件:
# users/management/commands/clean_inactive_users.py
from django.core.management.base import BaseCommand
from django.utils import timezone
from users.models import User
from datetime import timedelta

class Command(BaseCommand):
    help = 'Delete users who have not activated their account for more than 7 days'

    def handle(self, *args, **options):
        # 计算7天前的时间点
        cutoff_time = timezone.now() - timedelta(days=7)
        # 删除创建时间早于cutoff_time且未激活的用户
        deleted_count, _ = User.objects.filter(is_active=False, date_joined__lt=cutoff_time).delete()
        self.stdout.write(self.style.SUCCESS(f'Successfully deleted {deleted_count} inactive users'))
  1. 你可以手动执行命令清理:
python manage.py clean_inactive_users

也可以用系统定时任务(如Linux的crontab)或Django定时任务工具(如django-celery-beat)自动执行。

3. (进阶)允许真实邮箱主人重置未激活账号的密码

如果担心恶意用户设置了密码,导致真实主人激活后无法登录,可以修改激活流程,让用户先设置新密码再激活:

第一步:修改激活视图
def activate_email(request: HttpRequest, uidb64, token):
    try:
        uid = force_str(urlsafe_base64_decode(uidb64))
        user = User.objects.get(pk=uid)
    except (TypeError, ValueError, OverflowError, User.DoesNotExist):
        user = None
    
    if user and accound_activate_token.check_token(user, token):
        # 先不激活,跳转到设置密码页面
        request.session['activate_uid'] = uid
        return redirect('set_password_page')
    else:
        # 验证失败,返回错误页面
        return render(request, 'users/activation_failed.html')
第二步:创建设置密码的视图和模板
def set_password_page(request: HttpRequest):
    if request.method == 'POST':
        uid = request.session.get('activate_uid')
        if not uid:
            return redirect('register_page')
        try:
            user = User.objects.get(pk=uid, is_active=False)
        except User.DoesNotExist:
            return redirect('register_page')
        
        password = request.POST.get('password')
        # 可以添加密码强度验证(比如用Django的PasswordValidator)
        user.set_password(password)
        user.is_active = True
        user.save()
        del request.session['activate_uid']
        login(request, user)
        return redirect('main_page')
    
    return render(request, 'users/set_password.html')
第三步:创建set_password.html模板
<!-- users/set_password.html -->
<form method="post">
    {% csrf_token %}
    <label for="password">设置新密码:</label>
    <input type="password" name="password" id="password" required>
    <button type="submit">确认设置并激活账号</button>
</form>

测试验证

修改完代码后,你可以测试这几个场景:

  1. 用A邮箱注册不激活,再次用A邮箱注册——应该收到“已重新发送验证邮件”的提示,邮箱收到新的验证链接。
  2. 点击重新发送的验证链接——进阶方案会跳转到设置密码页面,设置后直接激活登录。
  3. 运行清理命令——超过7天的未激活账号会被自动删除。

这样一来,真实邮箱主人就不会被僵尸账号卡住,同时也能确保账号的安全性。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 11:32:59