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

Django DRF Simple JWT如何实现access token过期时跳转refresh token视图

实现步骤说明

首先先修复现有代码的基础问题,再根据你的场景选择自动刷新方案:

第一步:修复现有代码错误

1. 补充LoginView的refresh token存储

你当前的登录逻辑只存了access token到Cookie,没有存refresh token,无法执行刷新操作,修改LoginView代码:

class LoginView(APIView):
    def post(self, request, format=None):
        data = request.data
        response = Response()        
        email = data.get('email', None)
        password = data.get('password', None)
        user = authenticate(email=email, password=password)
        if user is not None:
            if user.is_active:
                data = get_tokens_for_user(user)
                # 存储access token
                response.set_cookie(
                    key = settings.SIMPLE_JWT['AUTH_COOKIE'], 
                    value = data["access"],
                    expires = settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
                    secure = settings.SIMPLE_JWT['AUTH_COOKIE_SECURE'],
                    httponly = settings.SIMPLE_JWT['AUTH_COOKIE_HTTP_ONLY'],
                    samesite = settings.SIMPLE_JWT['AUTH_COOKIE_SAMESITE']
                )
                # 新增:存储refresh token
                response.set_cookie(
                    key = "refresh_token", 
                    value = data["refresh"],
                    expires = settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
                    secure = settings.SIMPLE_JWT['AUTH_COOKIE_SECURE'],
                    httponly = settings.SIMPLE_JWT['AUTH_COOKIE_HTTP_ONLY'],
                    samesite = settings.SIMPLE_JWT['AUTH_COOKIE_SAMESITE']
                )
                csrf.get_token(request)
                response.data = {"Success" : "Login successfully","data":data}
                return response
            else:
                return Response({"No active" : "This account is not active!!"}, status=status.HTTP_404_NOT_FOUND)
        else:
            return Response({"Invalid" : "Invalid email or password!!"}, status=status.HTTP_404_NOT_FOUND)

2. 修正settings.py的认证类配置

你当前配置用的是默认JWT认证类,要替换成你自定义的CustomAuthentication:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'api.authenticate.CustomAuthentication', # 修改为自定义认证类路径
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated', # 需认证的接口默认用这个,公开接口单独加AllowAny
    ),
}

3. 适配Simple JWT的刷新逻辑

你当前urls.py里的代码是老版本JWT库的逻辑,和你用的Simple JWT不兼容,删除旧代码,自定义适配Cookie的刷新视图:
在views.py新增刷新视图:

from rest_framework_simplejwt.views import TokenRefreshView
from rest_framework_simplejwt.exceptions import InvalidToken, TokenError

class CookieTokenRefreshView(TokenRefreshView):
    def post(self, request, *args, **kwargs):
        # 从Cookie读取refresh token
        refresh_token = request.COOKIES.get('refresh_token')
        if not refresh_token:
            return Response({"detail": "Refresh token缺失"}, status=status.HTTP_401_UNAUTHORIZED)
        request.data['refresh'] = refresh_token
        serializer = self.get_serializer(data=request.data)
        try:
            serializer.is_valid(raise_exception=True)
        except TokenError as e:
            raise InvalidToken(e.args[0])
        # 刷新成功后更新access token的Cookie
        response = Response(serializer.validated_data, status=status.HTTP_200_OK)
        response.set_cookie(
            key = settings.SIMPLE_JWT['AUTH_COOKIE'], 
            value = serializer.validated_data["access"],
            expires = settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
            secure = settings.SIMPLE_JWT['AUTH_COOKIE_SECURE'],
            httponly = settings.SIMPLE_JWT['AUTH_COOKIE_HTTP_ONLY'],
            samesite = settings.SIMPLE_JWT['AUTH_COOKIE_SAMESITE']
        )
        # 如果开启了refresh token轮转,同步更新refresh token的Cookie
        if settings.SIMPLE_JWT.get('ROTATE_REFRESH_TOKENS', False):
            response.set_cookie(
                key = 'refresh_token', 
                value = serializer.validated_data["refresh"],
                expires = settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
                secure = settings.SIMPLE_JWT['AUTH_COOKIE_SECURE'],
                httponly = settings.SIMPLE_JWT['AUTH_COOKIE_HTTP_ONLY'],
                samesite = settings.SIMPLE_JWT['AUTH_COOKIE_SAMESITE']
            )
        return response

修改urls.py的刷新路径配置:

urlpatterns = [
    path('login/', views.LoginView.as_view(), name="login"),
    path('refresh/',views.CookieTokenRefreshView.as_view(), name="refresh"),
]

第二步:选择自动刷新方案

不建议直接重定向到refresh视图,因为refresh接口需要POST请求,普通302重定向会转为GET请求无法正常刷新,推荐以下两种方案:

方案1:前端配合实现(前后端分离场景推荐)

Simple JWT默认在access token过期时会返回401状态码,错误标识为token_not_valid,前端捕获到该响应时,自动发起POST请求到/refresh/接口,拿到新的access token后重新发起原来的发布帖子请求即可。如果刷新接口也返回401,说明refresh token也已过期,再跳转登录页。

方案2:后端中间件自动刷新(用户无感知)

自定义中间件自动捕获token过期异常,内部完成刷新后继续处理原请求:
新建middleware.py:

from rest_framework_simplejwt.exceptions import TokenExpiredError
from django.conf import settings
import json

class AutoRefreshTokenMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        # 检查是否是access token过期的401响应
        if response.status_code == 401:
            try:
                content = json.loads(response.content)
                if content.get('code') == 'token_not_valid' and 'expired' in content.get('detail', ''):
                    # 构造刷新请求
                    refresh_request = request.__class__(request.META)
                    refresh_request.method = 'POST'
                    refresh_request.data = {}
                    refresh_response = views.CookieTokenRefreshView.as_view()(refresh_request)
                    if refresh_response.status_code == 200:
                        # 刷新成功,更新请求中的access token,重新处理原请求
                        request.COOKIES[settings.SIMPLE_JWT['AUTH_COOKIE']] = refresh_response.data['access']
                        new_response = self.get_response(request)
                        # 同步新的Cookie到响应
                        for k, v in refresh_response.cookies.items():
                            new_response.set_cookie(
                                k, v.value, expires=v['expires'], secure=v['secure'],
                                httponly=v['httponly'], samesite=v['samesite'], path=v['path']
                            )
                        return new_response
            except:
                # 刷新失败直接返回原401响应
                pass
        return response

把中间件加到settings.py的MIDDLEWARE列表中:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'api.middleware.AutoRefreshTokenMiddleware', # 新增到这个位置
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    # 剩余原有中间件保持不变
]

这个方案前端不需要做任何修改,刷新过程用户完全无感知。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 20:06:03