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

基于Djoser JWT实现登录同步获取验证令牌及登出流程的方法咨询

实现JWT登录验证与安全登出流程(基于Djoser)

一、登录时同时获取并验证JWT令牌

Djoser默认的/jwt/create/只返回令牌,要实现“获取+验证”的合并流程,有两种方式:

前端直接联动现有端点

无需修改后端,前端完成两次请求即可:

  • 调用POST /jwt/create/,传入username和password,拿到access和refresh令牌
  • 立即发起POST /jwt/verify/,请求体携带{"token": "<access_token>"},验证令牌有效性
  • 验证通过后,将令牌存入HttpOnly Cookie(推荐)或localStorage,完成登录流程

后端自定义合并端点

如果想让前端一次请求完成操作,可自定义视图合并令牌生成与验证逻辑:

from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer, TokenVerifySerializer

class LoginWithVerifyView(APIView):
    def post(self, request):
        # 生成JWT令牌
        obtain_serializer = TokenObtainPairSerializer(data=request.data)
        obtain_serializer.is_valid(raise_exception=True)
        tokens = obtain_serializer.validated_data
        
        # 验证access令牌有效性
        verify_serializer = TokenVerifySerializer(data={"token": tokens["access"]})
        verify_serializer.is_valid(raise_exception=True)
        
        return Response(tokens)

在urls.py中添加路由:

path('jwt/login-verify/', LoginWithVerifyView.as_view(), name='login_with_verify'),

前端直接请求该端点即可获取已验证的令牌。

二、登出时用刷新令牌处理(含令牌更新与失效)

Djoser没有内置JWT登出端点,因为JWT本身是无状态的,需手动处理令牌生命周期。针对你的需求,分两种场景实现:

场景1:登出前用刷新令牌获取新访问令牌

如果需要在登出前更新一次访问令牌(比如收尾操作需要有效令牌),前端可先调用刷新端点再清除本地令牌:

async function handleLogout() {
  // 从Cookie中获取refresh令牌(推荐用Cookie存储)
  const refreshToken = document.cookie.split('; ')
    .find(row => row.startsWith('refresh_token='))?.split('=')[1];
  
  // 先请求新的access令牌
  if (refreshToken) {
    await fetch('/jwt/refresh/', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({"refresh": refreshToken})
    });
  }

  // 清除客户端存储的所有令牌
  document.cookie = 'access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
  document.cookie = 'refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
  
  window.location.href = '/login/';
}

场景2:安全登出(失效刷新令牌)

要彻底禁止已登出用户用旧刷新令牌获取新令牌,需在后端存储有效刷新令牌列表,登出时移除对应令牌:

  1. 创建存储有效刷新令牌的模型
from django.db import models
from django.contrib.auth import get_user_model
from datetime import datetime

User = get_user_model()

class ActiveRefreshToken(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    token = models.CharField(max_length=500, unique=True)
    expires_at = models.DateTimeField()

    class Meta:
        indexes = [models.Index(fields=['token'])]
  1. 扩展令牌生成视图,存入新刷新令牌
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.tokens import RefreshToken

class CustomTokenObtainPairView(TokenObtainPairView):
    def post(self, request, *args, **kwargs):
        response = super().post(request, *args, **kwargs)
        refresh_token = response.data.get('refresh')
        
        if refresh_token:
            token_obj = RefreshToken(refresh_token)
            ActiveRefreshToken.objects.create(
                user=self.request.user,
                token=refresh_token,
                expires_at=datetime.fromtimestamp(token_obj['exp'])
            )
        return response
  1. 自定义登出视图,移除刷新令牌
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.exceptions import InvalidToken

class LogoutView(APIView):
    permission_classes = [IsAuthenticated]
    
    def post(self, request):
        refresh_token = request.data.get('refresh')
        if not refresh_token:
            return Response({"error": "Refresh token required"}, status=400)
        
        try:
            ActiveRefreshToken.objects.get(token=refresh_token).delete()
            return Response({"detail": "Successfully logged out"}, status=200)
        except ActiveRefreshToken.DoesNotExist:
            raise InvalidToken("Refresh token is invalid or already revoked")
  1. 扩展刷新视图,验证令牌有效性
from rest_framework_simplejwt.views import TokenRefreshView
from rest_framework_simplejwt.exceptions import InvalidToken

class CustomTokenRefreshView(TokenRefreshView):
    def post(self, request, *args, **kwargs):
        refresh_token = request.data.get('refresh')
        if not ActiveRefreshToken.objects.filter(token=refresh_token).exists():
            raise InvalidToken("Refresh token has been revoked")
        return super().post(request, *args, **kwargs)
  1. 更新路由,替换默认端点
from django.urls import path
from .views import CustomTokenObtainPairView, CustomTokenRefreshView, LogoutView

urlpatterns = [
    path('jwt/create/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('jwt/refresh/', CustomTokenRefreshView.as_view(), name='token_refresh'),
    path('jwt/logout/', LogoutView.as_view(), name='logout'),
    # 保留Djoser其他端点...
]

前端登出时,只需调用POST /jwt/logout/并传入refresh令牌,后端会将其从有效列表中移除,之后该令牌无法再用于刷新获取新的access令牌。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 07:35:26