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

如何基于django-rest-framework-jwt实现一次性验证码免密自动登录并获取JWT?

实现无用户名密码的JWT登录(基于Django 2 + DRF + JWT)

当然可以实现啦!默认的django-rest-framework-jwt确实依赖用户名密码来生成Token,但我们可以通过扩展逻辑,结合一次性登录令牌(也就是你说的生成码),完全实现无密码登录并获取JWT的需求。下面是具体的实现步骤:

1. 先做基础:添加一次性登录令牌模型

首先需要一个模型来存储一次性的登录凭证,用来关联用户、记录令牌状态和过期时间:

# models.py
from django.db import models
from django.contrib.auth.models import User
import uuid
from datetime import timedelta
from django.utils import timezone

class OneTimeLoginToken(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    token = models.UUIDField(default=uuid.uuid4, unique=True)  # 用UUID生成不可猜测的令牌
    created_at = models.DateTimeField(auto_now_add=True)
    expires_at = models.DateTimeField()
    is_used = models.BooleanField(default=False)  # 标记令牌是否已使用

    def save(self, *args, **kwargs):
        # 默认设置令牌1小时后过期,可根据需求调整时长
        if not self.expires_at:
            self.expires_at = timezone.now() + timedelta(hours=1)
        super().save(*args, **kwargs)
    
    def is_valid(self):
        # 验证令牌是否有效:未使用且未过期
        return not self.is_used and timezone.now() < self.expires_at

2. 生成带令牌的登录链接

创建一个视图来生成包含令牌的链接,比如你可以在用户需要无密码登录时(比如找回密码、系统主动邀请)调用这个接口,把链接通过邮件/短信发给用户:

# views.py
from django.http import HttpResponse
from django.contrib.auth.models import User
from .models import OneTimeLoginToken

def generate_login_link(request, user_id):
    try:
        user = User.objects.get(id=user_id)
        # 先清理该用户已有的未使用令牌,避免重复生成
        OneTimeLoginToken.objects.filter(user=user, is_used=False).delete()
        # 创建新的一次性令牌
        token_obj = OneTimeLoginToken.objects.create(user=user)
        # 构造前端登录页面的链接,把令牌作为参数带上
        login_link = f"https://你的前端域名.com/login?token={token_obj.token}"
        # 这里可以添加发送邮件/短信的逻辑,把login_link推送给用户
        return HttpResponse(f"登录链接已生成:{login_link}")
    except User.DoesNotExist:
        return HttpResponse("目标用户不存在", status=404)

3. 扩展JWT逻辑:验证令牌并生成JWT

接下来需要自定义一个DRF视图,接收前端传来的一次性令牌,验证通过后直接生成JWT返回给前端:

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import OneTimeLoginToken
from rest_framework_jwt.settings import api_settings

# 导入JWT的核心处理函数
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

class OneTimeLoginView(APIView):
    def post(self, request):
        token_str = request.data.get('token')
        if not token_str:
            return Response({"error": "登录令牌不能为空"}, status=status.HTTP_400_BAD_REQUEST)
        
        try:
            token_obj = OneTimeLoginToken.objects.get(token=token_str)
            # 验证令牌是否有效
            if not token_obj.is_valid():
                return Response({"error": "令牌已过期或已被使用"}, status=status.HTTP_400_BAD_REQUEST)
            
            # 标记令牌为已使用,防止重复登录
            token_obj.is_used = True
            token_obj.save()
            
            # 生成JWT Token
            payload = jwt_payload_handler(token_obj.user)
            jwt_token = jwt_encode_handler(payload)
            
            return Response({
                "token": jwt_token,
                "user_id": token_obj.user.id,
                "username": token_obj.user.username
            }, status=status.HTTP_200_OK)
        except OneTimeLoginToken.DoesNotExist:
            return Response({"error": "无效的登录令牌"}, status=status.HTTP_404_NOT_FOUND)

4. 配置URL路由

把上面的视图添加到项目路由中,让接口可以被访问:

# urls.py
from django.urls import path
from .views import generate_login_link, OneTimeLoginView

urlpatterns = [
    # 生成登录链接的接口(建议只给内部/管理员调用,不要对外开放)
    path('generate-login-link/<int:user_id>/', generate_login_link, name='generate-login-link'),
    # 验证令牌并获取JWT的接口
    path('api/login/one-time/', OneTimeLoginView.as_view(), name='one-time-login'),
]

几个关键注意事项

  • 令牌安全性:一定要用UUID这类不可猜测的字符串做令牌,同时设置合理的过期时间(比如1小时),降低被滥用的风险。
  • 权限控制:生成登录链接的generate-login-link接口必须严格控制权限,比如只允许管理员或者系统内部逻辑调用,绝对不能对外开放。
  • JWT配置:可以在settings.py里调整JWT的过期时间等参数,比如:
# settings.py
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': timedelta(days=7),  # 设置JWT的有效期
    'JWT_ALLOW_REFRESH': True,  # 允许刷新Token
}

这样一来,用户点击带令牌的链接后,前端把令牌发送到/api/login/one-time/接口,就能拿到JWT Token,之后就可以用这个Token进行正常的DRF接口认证了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:21:18