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
相关产品推荐
相关产品推荐

