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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 22:34:58