Django自定义认证失效:用户与Agent均无法登录求助
问题概述
开发Django网站时设置了独立的CustomUser和Agent用户模型,Agent无需注册为User即可独立注册。注册时密码已在Admin面板确认被正确哈希,但两类用户登录均失败,提示“请输入正确的用户名和密码。注意两个字段均区分大小写。”
核心错误点及修复
1. Agent模型手动定义password字段,破坏AbstractBaseUser密码管理逻辑
AbstractBaseUser已内置密码管理机制,手动添加password = models.CharField(max_length=200)会覆盖原有字段,导致密码验证异常。同时agent_email应使用EmailField而非CharField,last_login需改为auto_now以记录每次登录时间。
修复models.py中Agent类:
class Agent(AbstractBaseUser, PermissionsMixin): username = models.CharField(max_length=100, unique=True) name = models.CharField(max_length=100) agent_email = models.EmailField(unique=True) # 修正字段类型 # 移除手动定义的password字段 image = models.ImageField(upload_to='Images/') bio = models.TextField() instagram = models.URLField(max_length=100) twitter = models.URLField(max_length=100) facebook = models.URLField(max_length=100) linkedin = models.URLField(max_length=100) is_featured = models.BooleanField(default=False) last_login = models.DateTimeField(auto_now=True) # 修正登录时间更新逻辑 is_active = models.BooleanField(default=True) slug = AutoSlugField(populate_from='name') is_staff = models.BooleanField(default=False) groups = models.ManyToManyField(Group, related_name='agent_set', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.') user_permissions = models.ManyToManyField(Permission, related_name='agent_set', blank=True, help_text='Specific permissions for this user.') objects = AgentManager() USERNAME_FIELD = 'username' EMAIL_FIELD = 'agent_email' def get_absolute_url(self): return reverse('agent-dashboard', kwargs={'slug': self.slug}) def __str__(self): return self.name
2. 认证后端get_user方法未处理Agent用户
原CustomAuthBackend的get_user仅查找CustomUser,登录Agent后系统无法识别该用户,导致后续验证失败。同时Agent的用户名/邮箱匹配应改为不区分大小写,与User逻辑保持一致。
修复backends.py:
from django.contrib.auth.models import Group, Permission from .models import Agent UserModel = get_user_model() class CustomAuthBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): user_type = kwargs.pop('user_type', None) if user_type == 'user': try: user = UserModel.objects.get(Q(username__iexact=username) | Q(email__iexact=username)) except UserModel.DoesNotExist: return None if user.check_password(password) and self.user_can_authenticate(user): return user elif user_type == 'agent': try: # 改为不区分大小写匹配用户名/邮箱 agent = Agent.objects.get(Q(username__iexact=username) | Q(agent_email__iexact=username)) except Agent.DoesNotExist: return None if agent.check_password(password) and self.user_can_authenticate(agent): return agent def get_user(self, user_id): # 先查找CustomUser,再查找Agent try: return UserModel.objects.get(pk=user_id) except UserModel.DoesNotExist: try: return Agent.objects.get(pk=user_id) except Agent.DoesNotExist: return None
3. 自定义登录表单需指定对应模型,避免默认验证逻辑冲突
原UserSignInForm和AgentSignInForm继承AuthenticationForm,默认使用AUTH_USER_MODEL验证,需重写get_user_model方法指定对应模型,并调整Agent表单的clean逻辑。
修复forms.py:
from django.contrib.auth.forms import AuthenticationForm from .models import Agent, CustomUser class UserSignInForm(AuthenticationForm): user_type = forms.CharField(initial='user', widget=forms.HiddenInput(), required=False) def get_user_model(self): return CustomUser class AgentSignInForm(AuthenticationForm): user_type = forms.CharField(initial='agent', widget=forms.HiddenInput(), required=False) def get_user_model(self): return Agent # 重写clean方法,避免默认验证逻辑报错 def clean(self): username = self.cleaned_data.get('username') password = self.cleaned_data.get('password') if username and password: pass # 验证逻辑留给后端处理 return self.cleaned_data
4. Agent注册/登录视图的后端指定错误及slug获取时机问题
原AgentRegisterView中登录时指定的后端不存在,AgentLoginView中登录成功前无法获取self.request.user.slug。
修复views.py:
class AgentRegisterView(CreateView): template_name = 'users/agent_register.html' form_class = AgentRegisterForm model = Agent def form_valid(self, form): response = super().form_valid(form) # 使用正确的后端名称 login(self.request, self.object, backend="users.backends.CustomAuthBackend") return HttpResponseRedirect(self.object.get_absolute_url()) class AgentLoginView(LoginView): template_name = 'users/agent_login.html' form_class = AgentSignInForm def get_success_url(self): # 登录成功后self.request.user已为Agent实例 return reverse_lazy('agent-dashboard', kwargs={'slug': self.request.user.slug})
5. AgentManager的create_user方法参数与模型字段不匹配
原AgentManager的create_user接收email参数,但Agent模型中对应字段为agent_email,导致创建超级用户出错。
修复models.py中的AgentManager:
class AgentManager(BaseUserManager): def create_user(self, username, agent_email, password=None, **extra_fields): if not agent_email: raise ValueError('The Email field must be set') agent_email = self.normalize_email(agent_email) user = self.model(username=username, agent_email=agent_email, **extra_fields) user.set_password(password) user.save(using=self._db) return user def create_superuser(self, username, agent_email, password=None, **extra_fields): extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', True) return self.create_user(username, agent_email, password, **extra_fields)
6. Agent登出视图的无效逻辑
原agent_logout中尝试修改agent.is_authenticated,但该属性是只读属性,无需手动修改。
修复views.py中的agent_logout:
def agent_logout(request): logout(request) messages.info(request, "") return redirect('home')
后续操作
- 确保settings.py中已配置
AUTH_USER_MODEL = 'users.CustomUser' - 运行
python manage.py makemigrations和python manage.py migrate同步模型变更 - 测试登录时可在authenticate方法中添加print语句,排查用户查询及密码验证结果
内容的提问来源于stack exchange,提问作者sasuke

