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

Django多表登录认证问题:自定义模型会话绑定失效及多表适配

多用户表登录认证解决方案

问题背景

在hostlogin应用中创建了HostTable模型用于教师登录认证,默认authenticate()仅支持Auth User表,因此编写了自定义认证函数,但login()无法将会话绑定到HostTable用户。设置AUTH_USER_MODEL ='hostlogin.HostTable'虽能解决会话问题,但会导致admin后台失效,且后续需要用HostTable处理教师登录(127.0.0.1:8081/tlogin/)、StudentTable处理学生登录(127.0.0.1:8081/slogin/),需实现多表分别登录认证的功能。

现有代码

models.py

from django.db import models
from django.utils import timezone
from django.contrib.auth.models import AbstractUser,AbstractBaseUser,PermissionsMixin
from django.contrib.auth.hashers import make_password, check_password


class HostTable(AbstractBaseUser):
    pid=models.AutoField(primary_key=True,auto_created=True)
    username=models.CharField(max_length=50,unique=True)
    password=models.CharField(max_length=128)
    #...其他字段...
    createdtime =models.DateTimeField(default=timezone.now)
    last_login=models.DateTimeField(default=timezone.now)
    is_authenticated=models.BooleanField(default=True)
    is_active   = models.BooleanField(default=True)
    is_staff    = models.BooleanField(default=False)
    USERNAME_FIELD = 'username'

    REQUIRED_FIELDS=['hostname','ownername','password']
    def save(self, *args, **kwargs):
        self.password = make_password(self.password)
        super(HostTable, self).save(*args, **kwargs)

    def __str__(self):
        return self.hostname

views.py

def hostlogin(request):
    if request.method == 'POST':
        form = UserLoginForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            print('view',username,password)
            user = hostTableAuth.authenticate(request,username,password)
            print('view',user)
            if user is not None:
                login(request,user)
                messages.success(request, f' welcome {username} !!')
                # print(request.META['REMOTE_USER'])
                print('r',request.user)
                print('r',request.user.is_authenticated)
                print('s',request.session)
                request.session['username']=username
                return redirect('home')
            else:
                messages.info(request, f'account done not exit plz sign in')
    form = UserLoginForm()
    return render(request, 'hostlogin/login.html', {'form':form, 'title':'log in'})

customeAuth.py

from django.contrib.auth.backends import BaseBackend,ModelBackend
from django.contrib.auth.hashers import check_password
from .models import HostTable


class hostTableAuth(ModelBackend):
      def authenticate(self, request,username, password):
        user=HostTable.objects.filter(username=username).first()
        print('user',username,password,user.password)
        if user is not None:
            login_valid = user.username == username
            pwd_valid = check_password(password, user.password)
            print(login_valid,pwd_valid)
            if login_valid and pwd_valid:
                try:
                    user = HostTable.objects.get(username=username)
                except HostTable.DoesNotExist:
                    # 创建新用户,无需设置密码,因为只验证settings.py中的密码
                    user = HostTable(username=username)
                    user.is_staff = True
                    user.is_superuser = True
                    user.save()
                print('auth1',user)
                return user
        else:
            print('auth2',user)

            return None

      def get_user(self, user_id):
        try:
            return HostTable.objects.get(pk=user_id)
        except HostTable.DoesNotExist:
            return None

解决方案

Django默认会话机制依赖AUTH_USER_MODEL,要实现多表登录,需自定义会话处理逻辑,同时保留默认User表用于admin后台。以下是两种可行方案:

方案1:自定义会话绑定+中间件识别

步骤1:修改登录视图

登录成功后,在会话中存储用户类型和ID,补充默认login()的不足:

def hostlogin(request):
    if request.method == 'POST':
        form = UserLoginForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            user = hostTableAuth.authenticate(request,username,password)
            if user is not None:
                login(request,user)
                # 新增:存储用户类型和ID到会话
                request.session['user_type'] = 'host'
                request.session['user_id'] = user.pid
                messages.success(request, f' welcome {username} !!')
                return redirect('home')
            else:
                messages.info(request, f'账号不存在,请检查后重试')
    form = UserLoginForm()
    return render(request, 'hostlogin/login.html', {'form':form, 'title':'教师登录'})

步骤2:编写自定义中间件

创建hostlogin/middleware.py,在请求处理时根据会话标识加载对应表的用户:

from django.utils.deprecation import MiddlewareMixin
from .models import HostTable, StudentTable

class MultiUserMiddleware(MiddlewareMixin):
    def process_request(self, request):
        # 仅当用户未通过默认认证时处理
        if request.user.is_anonymous:
            user_type = request.session.get('user_type')
            user_id = request.session.get('user_id')
            if user_type and user_id:
                if user_type == 'host':
                    try:
                        user = HostTable.objects.get(pid=user_id)
                        request.user = user
                    except HostTable.DoesNotExist:
                        # 清除无效会话
                        del request.session['user_type']
                        del request.session['user_id']
                elif user_type == 'student':
                    try:
                        user = StudentTable.objects.get(pid=user_id)
                        request.user = user
                    except StudentTable.DoesNotExist:
                        del request.session['user_type']
                        del request.session['user_id']

在settings.py中注册中间件(放在AuthenticationMiddleware之后):

MIDDLEWARE = [
    # ...其他中间件...
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'hostlogin.middleware.MultiUserMiddleware',
]

方案2:扩展认证后端支持多用户类型

步骤1:修改认证后端

让后端能根据登录视图传入的user_type处理不同用户表:

from django.contrib.auth.backends import ModelBackend
from .models import HostTable, StudentTable

class MultiUserAuthBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, user_type=None, **kwargs):
        if not user_type:
            return None
        
        # 根据用户类型选择对应模型
        UserModel = HostTable if user_type == 'host' else StudentTable
        
        try:
            user = UserModel.objects.get(username=username)
        except UserModel.DoesNotExist:
            return None
        
        # 验证密码
        if user.check_password(password):
            # 临时存储用户类型,后续会话使用
            setattr(user, 'user_type', user_type)
            return user
    
    def get_user(self, user_id):
        # 结合会话中的user_type查询,需配合中间件实现,参考方案1的逻辑
        return None

步骤2:修改登录视图

调用认证时传入user_type参数:

# 教师登录视图
def hostlogin(request):
    if request.method == 'POST':
        form = UserLoginForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data.get('username')
            password = form.cleaned_data.get('password')
            # 传入user_type指定认证表
            user = authenticate(request, username=username, password=password, user_type='host')
            if user:
                login(request, user)
                request.session['user_type'] = 'host'
                request.session['user_id'] = user.pid
                return redirect('home')
    # ...其余代码...

# 学生登录视图同理,传入user_type='student'

关键注意事项

  • 保留默认AUTH_USER_MODEL = 'auth.User',确保admin后台正常使用
  • 自定义用户模型必须继承AbstractBaseUser,确保密码哈希、认证方法正常工作
  • 登出时需清除会话中的user_type和user_id标识,避免残留无效会话

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 02:07:52