Django默认Session在前后端分离中失效,因跨域导致Cookie不携带,需改用JWT鉴权;推荐djangorestframework-simplejwt,配置access短时效、refresh长时效,前端内存存access+localStorage存refresh,服务端配合Redis黑名单实现安全登出。

为什么 Django 默认 Session 在前后端分离里会失效
因为 Session 依赖 Cookie + 同源策略,而前端(Vue/React)通常跑在 http://localhost:3000,后端 Django 跑在 http://localhost:8000,跨域导致浏览器不自动携带 sessionid Cookie,request.user 直接变 AnonymousUser。
更麻烦的是,移动端、小程序、第三方调用等场景根本没法可靠传 Cookie —— 不是“能不能配 CORS 和 withCredentials”,而是它本就不该承担鉴权职责。
- 必须关闭
SESSION_COOKIE_SAMESITE = 'Lax'(默认值),改为'None',否则跨域请求压根不发 Cookie - 必须开启
SESSION_COOKIE_SECURE = True(仅 HTTPS),否则'None'不生效 —— 本地开发就得配 HTTPS 或直接弃用 - 即便全配对,iOS Safari、部分安卓 WebView 仍会随机丢 Cookie,线上投诉难复现、难排查
用 Django REST Framework + djangorestframework-simplejwt 实现 Token 鉴权
别手写 JWT,djangorestframework-simplejwt 已覆盖主流需求:登录发 token、刷新、黑名单(需额外配)、自定义 payload 字段。关键是配置别踩坑:
- 安装后在
settings.py中注册'rest_framework_simplejwt'到INSTALLED_APPS,并配置DEFAULT_AUTHENTICATION_CLASSES -
SIMPLE_JWT['ACCESS_TOKEN_LIFETIME']建议设为 15–30 分钟,别贪长;REFRESH_TOKEN_LIFETIME可设 7 天,但必须配合前端安全存储(httpOnlyCookie 不行,得存localStorage或内存) - 登录接口返回的
access和refresh字段名,前端必须严格匹配 —— 默认是access/refresh,别自己改错成token/refresh_token导致后续请求 401 - 所有需要鉴权的 API 视图,必须显式加
permission_classes = [IsAuthenticated],DRF 不会自动继承全局配置
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
<p>urlpatterns = [
path('api/login/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]前端怎么安全存和传 Token(尤其避免 XSS 泄露)
Token 存 localStorage 最方便,但 XSS 一打就穿;存内存(如 Vue 的 ref 或 React 的 useState)能防持久泄露,但刷新页面就丢 —— 折中方案是「内存 + 本地缓存 fallback」:
立即学习“Python免费学习笔记(深入)”;
- 登录成功后,把
access存内存,refresh存localStorage(带短有效期,比如 7 天) - 每次 API 请求前,从内存取
access;若为空,尝试用refresh换新access,成功后再放回内存 - 换 token 失败(
401或403),清空所有本地存储,跳转登录页 —— 别留着过期refresh干耗着 - 绝对不要把
access写进Cookie(除非httpOnly+SameSite=None+ HTTPS),否则 CSRF 风险又回来了
Token 黑名单和登出怎么处理才靠谱
JWT 本身无状态,登出=删前端 token,但攻击者若截获了未过期的 access,还能继续用。真要阻断,得服务端配合黑名单:
- 装
django-redis,用 Redis 存黑名单(key 是blacklist_{jti},value 是过期时间),比 DB 查快得多 - 登出时,把当前
access的jti(JWT ID)塞进黑名单,TTL 设为该 token 剩余有效期(datetime.now() + access_lifetime - current_iat) - 重写
SimpleJWTAuthentication.get_validated_token,在 decode 后查黑名单 —— 别漏掉这步,否则黑名单形同虚设 - 注意:黑名单只对
access有效,refresh一般不加黑名单(太重),靠缩短其生命周期+单次使用后失效(ROTATE_REFRESH_TOKENS=True)来缓解
Token 鉴权不是“换种方式登录”,而是把认证逻辑从服务端状态(Session)转移到客户端凭证(Token)+ 服务端轻量校验。真正难的不是发 token,是前端怎么拿、存、续、丢,以及服务端怎么平衡安全性与性能——尤其是黑名单的 TTL 计算和 Redis 过期策略,稍不注意就变成高延迟瓶颈。


















