如何在Django中避免同一手机号多设备同时登录?
解决Django中同一手机号多设备同时登录的问题
要实现同一手机号仅允许单设备登录,核心思路是跟踪每个手机号对应的有效会话,在用户验证或发起请求时校验会话的唯一性。结合你的需求,具体实现步骤如下:
1. 创建自定义会话跟踪表
首先在你的Django app中定义模型,存储手机号与有效会话的关联关系:
from django.db import models from django.utils import timezone class PhoneSession(models.Model): phone_number = models.CharField(max_length=11, unique=True) # 唯一约束确保一个手机号仅一条有效记录 session_key = models.CharField(max_length=40) last_activity = models.DateTimeField(default=timezone.now) class Meta: db_table = 'phone_session' # 自定义表名,可选 def update_activity(self): self.last_activity = timezone.now() self.save(update_fields=['last_activity'])
执行迁移创建表:
python manage.py makemigrations python manage.py migrate
2. 手机号验证通过后的会话处理
当用户手机号验证通过后,先清理该手机号的旧会话,再绑定新会话:
from django.contrib.sessions.models import Session from django.utils.timezone import now from django.shortcuts import redirect, render from .models import PhoneSession def phone_verify(request): if request.method == 'POST': phone_number = request.POST.get('phone') # 此处省略外部数据库验证手机号的逻辑,假设验证通过 # 1. 清理该手机号的旧会话 old_session_record = PhoneSession.objects.filter(phone_number=phone_number).first() if old_session_record: # 删除Django存储中的旧会话 try: Session.objects.get(session_key=old_session_record.session_key).delete() except Session.DoesNotExist: pass old_session_record.delete() # 2. 绑定当前会话与手机号 PhoneSession.objects.create( phone_number=phone_number, session_key=request.session.session_key, last_activity=now() ) # 将手机号存入当前会话,用于后续校验 request.session['phone_number'] = phone_number return redirect('dashboard') # 跳转至网站操作页面 return render(request, 'verify.html')
3. 自定义中间件校验会话唯一性
编写中间件,每次请求时检查当前会话是否为该手机号的唯一有效会话:
from django.shortcuts import redirect from django.utils.timezone import now from .models import PhoneSession from datetime import timedelta class PhoneSessionAuthMiddleware: def __init__(self, get_response): self.get_response = get_response self.session_timeout = timedelta(minutes=30) # 与django-session-timeout保持一致 def __call__(self, request): # 跳过无需校验的路径(如验证页) if request.path in ['/verify/', '/']: return self.get_response(request) phone_number = request.session.get('phone_number') if phone_number: valid_session = PhoneSession.objects.filter(phone_number=phone_number).first() if not valid_session: # 无有效会话,强制退出 del request.session['phone_number'] return redirect('verify') # 检查会话是否超时 if now() - valid_session.last_activity > self.session_timeout: valid_session.delete() del request.session['phone_number'] return redirect('verify') # 检查当前会话是否与有效会话匹配 if request.session.session_key != valid_session.session_key: del request.session['phone_number'] return redirect('verify') # 更新最后活动时间,维持会话活跃 valid_session.update_activity() response = self.get_response(request) return response
在settings.py的MIDDLEWARE列表中添加该中间件(需放在SessionMiddleware之后):
MIDDLEWARE = [ # ... 其他中间件 'django.contrib.sessions.middleware.SessionMiddleware', 'yourapp.middleware.PhoneSessionAuthMiddleware', # 替换为你的app路径 # ... 其他中间件 ]
4. 清理过期会话记录
django-session-timeout会自动清理Django会话,但PhoneSession表的过期记录需手动清理,可选两种方式:
- 定时任务:用
django-celery-beat创建每日定时任务:from django.utils.timezone import now from .models import PhoneSession from datetime import timedelta def clean_expired_sessions(): expire_time = now() - timedelta(minutes=30) PhoneSession.objects.filter(last_activity__lt=expire_time).delete() - 懒清理:在验证视图或中间件中顺带清理(适合小流量站点),直接加入上述代码片段即可。
5. 用户主动退出的处理
当用户点击退出时,清理会话关联记录:
from django.shortcuts import redirect from .models import PhoneSession def logout(request): phone_number = request.session.get('phone_number') if phone_number: PhoneSession.objects.filter(phone_number=phone_number).delete() request.session.flush() # 清空当前会话 return redirect('verify')
内容的提问来源于stack exchange,提问作者Tejas nayak
相关产品推荐
相关产品推荐

