Django自定义邮箱认证登录跳转失效、表单错误不触发问题咨询
Django 自定义邮箱登录问题排查与最佳实现
问题1:跳转后用户显示未登录
问题根因有两处:
- 自定义认证后端缺少Django强制要求实现的
get_user方法:Django在登录后从会话恢复用户身份时,会调用该方法根据用户ID查询用户,缺失该方法会导致会话校验失败,判定用户未登录。 - 异常捕获代码存在语法错误:
except user.DoesNotExist写法不合法,user是try块内才定义的变量,此处应该捕获Account.DoesNotExist。
修正后的认证后端代码:
from django.contrib.auth.backends import ModelBackend from .models import Account class EmailAuthenticationBackend(ModelBackend): def authenticate(self, request, email=None, password=None, **kwargs): try: user = Account.objects.get(email=email) # 额外加用户状态校验,避免禁用用户登录 if user.check_password(password) and self.user_can_authenticate(user): return user except Account.DoesNotExist: return None def get_user(self, user_id): try: return Account.objects.get(pk=user_id) except Account.DoesNotExist: return None
问题2:表单不抛出校验错误
问题根因有两处:
- 校验逻辑冗余无法触发:你已经调用了
authenticate方法校验邮箱和密码,只要方法返回有效用户就说明密码正确,后续再调用user.check_password(password)永远返回True,密码错误的异常永远不会触发。 - 抛出的是表单全局校验错误,如果你登录模板没有渲染
{{ form.non_field_errors }},错误信息就不会在页面展示。
修正后的表单代码:
from django import forms from django.contrib.auth import authenticate class UserLoginForm(forms.Form): email = forms.EmailField(label="邮箱") password = forms.CharField(label="密码", widget=forms.PasswordInput) def clean(self): cleaned_data = super().clean() email = cleaned_data.get("email") password = cleaned_data.get("password") if email and password: user = authenticate(email=email, password=password) if not user: raise forms.ValidationError("邮箱或密码输入错误") return cleaned_data
同时在accounts/user_login.html的表单位置添加全局错误渲染代码:
{% if form.non_field_errors %} <div class="error-notice"> {% for error in form.non_field_errors %} <p>{{ error }}</p> {% endfor %} </div> {% endif %}
邮箱登录通用最佳方案
最推荐的方案是直接修改用户模型的用户名字段,不需要自定义认证后端,可以完美适配Django原生所有认证逻辑,稳定性最高、代码量最少:
- 自定义用户模型时继承
AbstractUser,指定USERNAME_FIELD为email,移除原username字段的必填限制:
from django.contrib.auth.models import AbstractUser from django.db import models class Account(AbstractUser): username = None email = models.EmailField("邮箱", unique=True) USERNAME_FIELD = "email" REQUIRED_FIELDS = []
- 直接使用Django内置的
AuthenticationForm和LoginView,不需要重复编写表单、视图的校验逻辑,只需要在模板中把表单的username输入框调整为邮箱样式即可。 - 如果业务必须保留username字段,才需要使用自定义认证后端方案,注意必须给email字段添加唯一索引,避免多个用户绑定同一个邮箱。
内容的提问来源于stack exchange,提问作者yvl
相关产品推荐
相关产品推荐

