自定义User模型与认证后端下Django user_login_failed信号失效求助
问题描述
我是Django新手,为项目需求实现了自定义用户模型(Custom User)和自定义认证后端(Custom Authentication Backend),但现在无法触发user_login_failed Django信号——原本想用它限制用户登录尝试次数为3次。切换到自定义模型和后端前代码运行正常,现出现此问题,相关代码如下:
Signals.py
from django.contrib.auth.signals import (user_logged_in, user_logged_out, user_login_failed) from django.contrib.auth import get_user_model from django.db.models import F from django.dispatch import receiver Usermanagement = get_user_model() print("Sgnals usermanagement: ",Usermanagement) @receiver(user_logged_in,sender=Usermanagement) def login_success(sender,request,user, **kwargs): print("-------------------------") print("Login Success Signal...") print("Sender: ", sender) print("Request: ", request) print("Username: ",user.email) print("Password: ",user.password) print(f"Kwargs: {kwargs}") @receiver(user_login_failed,sender=Usermanagement) def login_failed(sender,credentials,request,**kwargs): print("-------------------------") print("Login failed Signal...") print("Sender: ", sender) print("Credentials: ", credentials) print("Request: ", request) print(f"Kwargs: {kwargs}")
apps.py
from django.apps import AppConfig class AccountsConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'accounts' def ready(self): import accounts.signals
init.py
default_app_config = "accounts.apps.AccountsConfig"
settings.py
AUTH_USER_MODEL = 'accounts.Usermanagement' AUTHENTICATION_BACKENDS = [ 'accounts.backends.EmailAuthBackend', 'django.contrib.auth.backends.ModelBackend', ]
models.py
class Usermanagement(AbstractBaseUser): emailid = models.CharField(db_column='EmailID', unique=True, max_length=45,default=1) # 其他字段省略 objects = UsermanagementCustomUserManager() USERNAME_FIELD = "emailid" EMAIL_FIELD = "emailid"
managers.py
class UsermanagementCustomUserManager(BaseUserManager): def create_user(self,emailid,firstname, password=None): if not emailid: raise ValueError('Users must have an email address') user = self.model( emailid=self.normalize_email(emailid), password=password, ) user.set_password(password) user.save(using=self._db) return user
backends.py
from django.contrib.auth.backends import BaseBackend from django.contrib.auth.hashers import make_password,check_password from django.contrib.auth import get_user_model Usermanagement = get_user_model() class EmailAuthBackend(BaseBackend): def authenticate(self,request,username=None,password=None): try: print("Trying the email backend!") user = Usermanagement.objects.get(emailid=username) print("Got the user") if user.password == password or user.check_password(password): return user except user.DoesNotExist: return None def get_user(self,user_id): try: print("Getting the user of the Email Bkacned") return Usermanagement.objects.get(pk=user_id) except Usermanagement.DoesNotExist: return None
views.py
from django.contrib.auth import login,logout ,authenticate from django.contrib.auth.forms import AuthenticationForm from django.shortcuts import render, redirect def loginPage(request): if request.method == 'POST': form = AuthenticationForm(request,data=request.POST) if form.is_valid(): email = form.cleaned_data.get('username') password = form.cleaned_data.get('password') print("EMAIL: ",email) print("PASSWORD: ",password) user = authenticate(request,username=email,password=password) print("Authenticated ",user) if user is not None: if user.is_active: login(request,user,backend='accounts.backends.EmailAuthBackend') return redirect("home") else: pass else: pass else: context = {"form" : form} return render(request,"loginPage.html",context=context) else: form = AuthenticationForm() context = {"form":form} return render(request,"loginPage.html",context=context)
问题原因及修复方案
1. 核心问题:自定义认证后端未主动发送user_login_failed信号
Django内置认证后端会自动在登录失败时发送user_login_failed信号,但自定义后端需要手动触发。你的EmailAuthBackend在认证失败时直接返回None,没有触发信号,导致接收器无法捕获。
2. 修复后的backends.py
from django.contrib.auth.backends import BaseBackend from django.contrib.auth.hashers import check_password from django.contrib.auth import get_user_model from django.contrib.auth.signals import user_login_failed Usermanagement = get_user_model() class EmailAuthBackend(BaseBackend): def authenticate(self, request, username=None, password=None): try: print("Trying the email backend!") user = Usermanagement.objects.get(emailid=username) print("Got the user") # 移除明文密码对比,只保留安全的哈希校验 if user.check_password(password): return user else: # 密码错误时发送登录失败信号 user_login_failed.send( sender=Usermanagement, credentials={'username': username}, request=request ) return None except Usermanagement.DoesNotExist: # 用户不存在时发送登录失败信号 user_login_failed.send( sender=Usermanagement, credentials={'username': username}, request=request ) return None def get_user(self, user_id): try: print("Getting the user of the Email Backend") return Usermanagement.objects.get(pk=user_id) except Usermanagement.DoesNotExist: return None
3. 额外优化点
- 移除了
user.password == password的明文对比逻辑,避免安全风险; - 修复了
except user.DoesNotExist的拼写错误,改为Usermanagement.DoesNotExist; - 确保信号发送时的
sender参数与信号接收器指定的一致,保证信号能被正确捕获。
内容的提问来源于stack exchange,提问作者Nipun
相关产品推荐
相关产品推荐

