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

Django中@login_required装饰器失效问题排查(用户未完成认证?)

问题原因与解决方案

我一眼就看出问题出在哪了——你的自定义User模型没有兼容Django的认证系统,导致login()函数没法正确完成用户会话的创建,所以@login_required装饰器一直认为你是未登录状态,才会反复重定向回登录页。

核心问题分析

Django的auth.login()函数依赖于用户模型实现特定的认证接口(比如会话存储、用户身份验证的标准方法),但你现在的User模型只是一个普通的models.Model子类,没有继承Django为认证系统设计的基类(AbstractBaseUser或AbstractUser)。这就导致:

  • login()无法在请求会话中正确标记用户已认证
  • @login_required检查request.user.is_authenticated时,结果始终是False

另外,虽然你用了check_password()验证密码,但自定义模型的密码字段也没有遵循Django的密码哈希存储规范(不过这不是当前重定向问题的核心)。

解决方案(推荐使用Django标准认证模型)

最省心的方式是改用Django自带的认证用户模型或扩展它,这样认证系统能无缝工作:

1. 修改自定义User模型

让你的User模型继承AbstractUser(它包含了所有标准认证字段和方法),只添加你需要的额外字段:

# models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

class User(AbstractUser):
    admin = models.BooleanField(default=False)
    # first_name、last_name、username、email这些字段AbstractUser已经自带,无需重复定义

然后在项目的settings.py中配置自定义用户模型:

AUTH_USER_MODEL = '你的应用名.User'  # 替换成实际的应用名称

2. 更新注册表单

改用Django的UserCreationForm来构建注册表单,它会自动处理密码哈希和确认:

# forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from .models import User

class RegisterForm(UserCreationForm):
    email = forms.EmailField(required=True)
    
    class Meta:
        model = User
        fields = ['first_name', 'last_name', 'username', 'email', 'password1', 'password2']

3. 优化登录视图

使用Django自带的authenticate()函数来验证用户,它会自动处理密码哈希验证和用户状态检查:

# views.py
from django.contrib.auth import authenticate, login
from django.shortcuts import render, redirect
from django.contrib import messages

def login_user(request):
    if request.method == "GET":
        return render(request, "main/login.html", {})
    else:
        username_or_email = request.POST['userfield']
        password = request.POST['passfield']
        
        # 先尝试用用户名登录
        user = authenticate(request, username=username_or_email, password=password)
        
        # 如果失败,尝试用邮箱匹配用户后再认证
        if not user:
            try:
                user_obj = User.objects.get(email=username_or_email)
                user = authenticate(request, username=user_obj.username, password=password)
            except User.DoesNotExist:
                user = None
        
        if user is not None:
            login(request, user)
            return redirect('homepage')
        else:
            messages.info(request, "*Sorry, invalid username/email or password")
            return redirect('login')

如果你坚持要用完全自定义的User模型(不推荐)

如果因为特殊需求必须用自己的模型结构,需要让它继承AbstractBaseUser和PermissionsMixin,并实现必要的管理器和方法:

# models.py
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models

class UserManager(BaseUserManager):
    def create_user(self, email, username, password=None, **extra_fields):
        if not email:
            raise ValueError('Users must have an email address')
        email = self.normalize_email(email)
        user = self.model(email=email, username=username, **extra_fields)
        user.set_password(password)  # 自动处理密码哈希
        user.save(using=self._db)
        return user

    def create_superuser(self, email, username, password=None, **extra_fields):
        extra_fields.setdefault('admin', True)
        extra_fields.setdefault('is_superuser', True)
        return self.create_user(email, username, password, **extra_fields)

class User(AbstractBaseUser, PermissionsMixin):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    username = models.CharField(max_length=100, unique=True)
    email = models.EmailField(unique=True)
    admin = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)  # 必须有这个字段表示用户是否可用

    objects = UserManager()

    USERNAME_FIELD = 'username'  # 设置登录时使用的字段(可以改为'email')
    REQUIRED_FIELDS = ['email']  # 创建超级用户时必填的字段

    def __str__(self):
        return self.username

    @property
    def is_staff(self):
        # 让admin用户拥有后台访问权限
        return self.admin

同样要在settings.py中配置AUTH_USER_MODEL,之后login()和@login_required就能正常工作了。

内容的提问来源于stack exchange,提问作者Cameron Grant

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 23:42:33