Django中root超级用户登录需supervisor审批的登录异常求助
Django Root用户登录审批流程异常问题
问题背景
项目中有root和supervisor两个超级用户,期望实现的登录审批流程:
- root登录后跳转至「等待授权」页面
- 系统向supervisor发送含明文OTP及验证链接的邮件
- supervisor输入OTP验证通过后,root自动跳转至着陆页,supervisor看到成功提示;验证失败则双方都看到失败提示
当前问题:OTP验证成功后,supervisor被重定向至着陆页,而root的等待页面无任何变化。
核心问题分析
从日志和代码来看,你在verify_otp视图中创建新HttpRequest并尝试登录root的操作仅修改了后端session数据,无法触发root前端页面的跳转;同时直接返回redirect('landingpage')会让supervisor的页面跳转到root的着陆页,不符合流程设计。
解决方案步骤
1. 完善OTP模型字段(可选但建议)
给otp_secret指定长度,避免数据库字段定义不规范:
# models.py class OTP(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) otp_secret = models.CharField(max_length=32) is_verified = models.BooleanField(default=False) def __str__(self): return self.user.email class UserSession(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) session = models.ForeignKey(Session, on_delete=models.CASCADE)
2. 重构verify_otp视图逻辑
移除无效的request对象创建操作,改为更新OTP验证状态,给supervisor返回专属提示页:
# views.py @login_required(login_url='custom_login') def verify_otp(request): logger.debug('inside verify_otp. request.user is %s', request.user) otp = request.POST.get('otp') logger.debug('inside verify_otp: otp is %s. Request.user is: %s', otp, request.user) try: latest_root_session = UserSession.objects.filter(user=user2, user__otp__is_verified=False).latest('session__expire_date') except ObjectDoesNotExist: messages.error(request, '当前无待审批的root用户会话') return render(request, 'something_went_wrong.html') if latest_root_session.user.id != user2.id: messages.error(request, '当前无待审批的root用户会话') return render(request, 'something_went_wrong.html') # 校验OTP格式 if not otp or otp == 'None': messages.error(request, 'OTP不能为空') return render(request, 'something_went_wrong.html') try: int_otp = int(otp) except ValueError: messages.error(request, 'OTP格式无效') return render(request, 'something_went_wrong.html') # 获取OTP对象并验证 otp_obj = OTP.objects.filter(user__pk=user2.id).first() if not otp_obj: messages.error(request, 'OTP授权失败,请重新登录') return render(request, 'something_went_wrong.html') totp = pyotp.TOTP(otp_obj.otp_secret, interval=otp_interval) verification_result = totp.verify(int_otp) if verification_result: # 更新OTP验证状态 otp_obj.is_verified = True otp_obj.save() # 给supervisor返回成功提示 messages.success(request, 'OTP验证成功,root用户已登录') return render(request, 'otp_success.html') # 新建该页面展示成功信息 else: # 标记验证失败状态 otp_obj.is_verified = False otp_obj.save() latest_root_session.session['otp_failed'] = True latest_root_session.session.save() messages.error(request, 'OTP授权失败') return render(request, 'something_went_wrong.html')
3. 给root的等待页面添加轮询机制
修改login_redirect.html,通过JavaScript定时检查验证状态:
<!-- templates/login_redirect.html --> <!DOCTYPE html> <html> <head> <title>等待授权</title> </head> <body> <h1>等待Supervisor授权...</h1> <div id="status"></div> <script> // 每3秒轮询一次验证状态 const poll = setInterval(() => { fetch('{% url "check_otp_status" %}') .then(res => res.json()) .then(data => { if (data.status === 'verified') { clearInterval(poll); window.location.href = '{% url "landingpage" %}'; } else if (data.status === 'failed') { clearInterval(poll); document.getElementById('status').textContent = 'OTP授权失败,请重新登录'; } }) .catch(err => console.error('轮询失败:', err)); }, 3000); </script> </body> </html>
4. 添加检查OTP状态的视图
# views.py from django.http import JsonResponse @login_required(login_url='custom_login') def check_otp_status(request): # 仅允许root用户访问 if not is_root_user(request.user): return JsonResponse({'status': 'unauthorized'}, status=403) otp_obj = OTP.objects.filter(user=request.user).first() if not otp_obj: return JsonResponse({'status': 'pending'}) if otp_obj.is_verified: # 验证成功后重置状态,避免重复登录 otp_obj.is_verified = False otp_obj.save() return JsonResponse({'status': 'verified'}) # 检查是否标记失败状态 if request.session.get('otp_failed'): del request.session['otp_failed'] return JsonResponse({'status': 'failed'}) return JsonResponse({'status': 'pending'})
5. 配置路由
在urls.py中新增状态检查路由:
from django.urls import path from . import views urlpatterns = [ # 其他路由... path('check-otp-status/', views.check_otp_status, name='check_otp_status'), path('enter-otp/', views.verify_otp, name='enter_otp'), ]
关键修改说明
- 移除无效操作:删除创建新
HttpRequest和调用login的代码,这些操作无法触发前端页面跳转,完全无效。 - 轮询触发跳转:通过前端定时轮询后端状态,是跨用户授权场景下触发页面跳转的标准方案。
- 分离用户行为:supervisor验证成功后仅看到专属成功提示,不会跳转到root的着陆页;root页面根据轮询结果自动跳转。
- 状态重置:验证成功后重置OTP的
is_verified状态,避免root下次登录时直接通过。
内容的提问来源于stack exchange,提问作者Kuchiki Byakuya
相关产品推荐
相关产品推荐

