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

自定义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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 11:55:24