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

