Django 4 + SimpleJWT 默认不支持无感刷新,因 TokenRefreshView 不区分 access 与 refresh 过期状态且不轮换 refresh token;需配置 ROTATE_REFRESH_TOKENS=True、BLACKLIST_AFTER_ROTATION=True,并自定义 CookieTokenRefreshView 支持从 cookie 读取 refresh。

为什么 Django 4 + djangorestframework-simplejwt 默认不支持无感刷新
因为 SimpleJWT 的 TokenRefreshView 要求客户端显式传入 refresh,且一旦过期就直接报 {"detail":"Token is invalid or expired"} —— 它不区分“access 过期但 refresh 仍有效”和“refresh 也过期”,更不会自动轮换新 refresh token。无感刷新的前提是:access 失效时,用旧 refresh 换出新 access + 新 refresh,且旧 refresh 失效(防重放)。Django 4 默认没开这个开关。
如何启用 SimpleJWT 的双令牌自动轮换
关键在配置 settings.py 中的 ROTATE_REFRESH_TOKENS 和 BLACKLIST_AFTER_ROTATION:
-
ROTATE_REFRESH_TOKENS = True:每次调用token_refresh时生成新 refresh token,并使旧 refresh 失效 -
BLACKLIST_AFTER_ROTATION = True:配合ROTATE_REFRESH_TOKENS,确保旧 token 进入黑名单(需提前装好django-redis或启用数据库黑名单) - 别漏掉
from datetime import timedelta,然后设REFRESH_TOKEN_LIFETIME = timedelta(days=7)(避免 refresh 太短导致频繁登录)
注意:若用数据库黑名单,要运行 python manage.py migrate;若用 Redis,确保 REDIS_URL 已配置且 SimpleJWT 能连上。
前端怎么做到“无感”——只发一次 refresh 请求
核心逻辑不是靠后端兜底,而是前端拦截 401 错误、自动补发 refresh、再重试原请求。常见陷阱:
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 多个并发请求同时 401,会触发多次 refresh,导致旧 refresh 被重复使用而报
"Token is blacklisted" - 没锁住 refresh 流程,造成 race condition
- 把
refresh存在 localStorage,被 XSS 窃取风险高(应存 httpOnly cookie)
实操建议:用 axios 的 interceptors.response + Promise queue 控制 refresh 单例;将 refresh 放在 httpOnly + Secure + SameSite=Lax 的 cookie 里(后端用 set_cookie 写入),前端请求时不带它,仅由浏览器自动携带。
自定义 TokenRefreshView 以支持 Cookie 读取 refresh
默认 TokenRefreshView 只从 request body 读 refresh 字段。要支持 cookie 方式,得继承并重写 post 方法:
from rest_framework_simplejwt.views import TokenRefreshView
from rest_framework_simplejwt.exceptions import InvalidToken
<p>class CookieTokenRefreshView(TokenRefreshView):
def post(self, request, *args, **kwargs):</p><h1>优先从 cookie 读 refresh,fallback 到 body</h1><pre class='brush:python;toolbar:false;'> refresh = request.COOKIES.get('refresh') or request.data.get('refresh')
if not refresh:
raise InvalidToken('No token provided')
request.data['refresh'] = refresh
return super().post(request, *args, **kwargs)然后在 urls.py 里替换路由:path('api/token/refresh/', CookieTokenRefreshView.as_view(), name='token_refresh')。别忘了给该 view 配置 CsrfViewMiddleware 白名单(如果用了 CSRF 保护),或改用 @csrf_exempt —— 但后者需确保 cookie 带 SameSite 且前端 origin 可控。
真正难的不是换 token,是让整个链路不暴露 refresh 给 JS、不因竞态崩掉、不因配置漏项导致黑名单失效。每一步都得对齐 SimpleJWT 的 token lifecycle 状态机,而不是只抄几行代码。

















