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

Django DRF如何保存用户设备避免每次登录都需执行2FA验证

DRF JWT 受信任设备2FA跳过实现方案

核心逻辑

用户首次在新设备完成2FA验证后,服务端为该设备签发唯一加密标识,存储为HttpOnly安全Cookie,后续同一设备登录时,校验标识有效则直接返回JWT凭证,跳过OTP发送流程。

具体实现步骤

1. 新增受信任设备存储模型

在项目models.py中新增UserTrustedDevice表,用于存储用户受信任设备信息:

from django.db import models
from django.contrib.auth import get_user_model
from django.utils import timezone

User = get_user_model()

class UserTrustedDevice(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='trusted_devices')
    device_identifier = models.CharField(max_length=255, unique=True) # 设备唯一标识
    device_name = models.CharField(max_length=100, default='未知设备') # 可选,用户备注设备名
    expired_at = models.DateTimeField() # 信任有效期
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        unique_together = ('user', 'device_identifier')

2. 修改登录逻辑,优先校验受信任设备

修改usecases.py中的UserLoginWithOTPUseCase._factory方法,用户名密码校验通过后,先检查是否存在有效受信任设备,存在则直接返回JWT,跳过OTP发送:

from django.utils import timezone
from .models import UserTrustedDevice
from rest_framework_simplejwt.tokens import RefreshToken

class UserLoginWithOTPUseCase(CreateUseCase, OTPMixin):
    # 原有__init__、execute方法保持不变
    def _factory(self):
        credentials = {
            'username': self._data['email'],
            'password': self._data['password']
        }
        self._user = authenticate(self._request, **credentials)
        if self._user is not None:
            # 新增:校验有效受信任设备
            trusted_device_token = self._request.COOKIES.get('trusted_device_token')
            if trusted_device_token:
                valid_device = UserTrustedDevice.objects.filter(
                    user=self._user,
                    device_identifier=trusted_device_token,
                    expired_at__gt=timezone.now()
                ).first()
                if valid_device:
                    # 直接返回JWT,跳过2FA
                    refresh = RefreshToken.for_user(self._user)
                    # 这里把结果返回给视图层构造响应即可
                    return {
                        'access': str(refresh.access_token),
                        'refresh': str(refresh),
                        'skip_2fa': True
                    }
            # 无有效受信任设备,走原有OTP发送逻辑
            code = self._generate_totp(
                user=self._user,
                purpose='2FA',
                interval=180
            )
            EmailVerificationEmail(
                context={
                    'code': code,
                    'uuid': self._user.id
                }
            ).send(to=[self._user.email])
        else:
            raise PermissionDenied(
                {
                    'authentication_error': _('User name or password not matched')
                }
            )

3. OTP验证通过后新增受信任设备记录

在OTP验证接口的逻辑中,新增remember_device参数,用户勾选后生成设备标识并写入Cookie:

# OTP验证接口逻辑示例(你原有OTP校验通过的位置新增以下代码)
import secrets
from datetime import timedelta
from django.conf import settings

# 假设已经完成OTP校验,拿到当前登录用户user
remember_device = request.data.get('remember_device', False)
response = Response({
    'access': str(refresh.access_token),
    'refresh': str(refresh)
})
if remember_device:
    # 生成随机唯一设备标识
    device_id = secrets.token_urlsafe(32)
    # 存储到受信任设备表
    UserTrustedDevice.objects.create(
        user=user,
        device_identifier=device_id,
        expired_at=timezone.now() + timedelta(days=30), # 信任有效期30天
        device_name=request.data.get('device_name', '未知设备')
    )
    # 写入HttpOnly安全Cookie
    response.set_cookie(
        'trusted_device_token',
        device_id,
        max_age=30*24*60*60,
        httponly=True,
        secure=not settings.DEBUG, # 生产环境强制HTTPS传输
        samesite='Lax'
    )
return response

安全注意事项

  • 受信任设备Cookie必须开启HttpOnly属性,防止XSS攻击窃取标识,生产环境必须开启Secure属性,仅HTTPS请求携带
  • 设备标识使用随机加密串,不要存储IP、UA等明文可篡改的信息,避免伪造
  • 提供用户设备管理入口,支持手动删除受信任设备,账号异常时可以一键清空所有信任记录
  • 信任有效期建议设置为30-90天,到期自动失效,需要重新完成2FA验证

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 17:48:01