默认只接受 Authorization: Bearer <token> 格式,前端若误传为 JWT 前缀或漏掉 Bearer,则 Django REST Framework 静默拒绝,返回 401;需严格统一请求头格式,并移除 CSRF 和 SessionAuthentication 干扰。

为什么直接用 SimpleJWT 的默认配置会登录成功但后续请求总返回 401?
因为默认只接受 Authorization: Bearer <token> 格式,而前端常误传成 Authorization: JWT <token> 或漏掉 Bearer 前缀。Django REST Framework 不会自动 fallback,也不会报具体原因,只静默拒绝。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 在前端 axios 或 fetch 中严格确保请求头为
headers: {'Authorization': `Bearer ${access_token}`} - 后端可临时加日志验证:重写
SimpleJWTTokensView的get_tokens_for_user,或在 middleware 中打印request.META.get('HTTP_AUTHORIZATION') - 检查是否启用了 CSRF 验证干扰——JWT 是无状态的,
SessionAuthentication和CSRFToken必须从DEFAULT_AUTHENTICATION_CLASSES中移除
如何让 refresh 接口真正“刷新”而不是返回新 token 后旧 token 还能用?
SimpleJWT 默认不黑名单旧 refresh token,只要它没过期,就一直有效。这不符合安全预期。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 启用
ROTATE_REFRESH_TOKENS = True和BLACKLIST_AFTER_ROTATION = True(需同时开启) - 安装并运行
django-redis或django-cache作为黑名单后端,否则blacklist表不会被写入 - 确保数据库迁移已执行:
python manage.py migrate(simplejwt提供了BlacklistedToken模型) - 注意:refresh token 一旦被使用且启用旋转,原 token 就立即失效;但 access token 仍按自身有效期运行,无法强制提前作废
如何自定义 access_token 的 payload 加入用户角色或部门字段?
不能直接改 TokenObtainPairView 返回值,必须继承并重写 get_token 方法,再在 token 实例上添加字段。
实操示例:
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework_simplejwt.tokens import AccessToken
<p>class CustomTokenObtainPairView(TokenObtainPairView):
@classmethod
def get_token(cls, user):
token = super().get_token(user)</p><h1>加入自定义字段</h1><pre class='brush:python;toolbar:false;'> token['role'] = user.profile.role # 假设 profile 是 OneToOne 关联
token['dept_id'] = user.profile.dept_id
return token注意:
- 字段值必须是 JSON 序列化安全类型(str/int/bool/list/dict),不能放 model 实例或 datetime 对象
- 不要在 payload 放敏感信息(如密码哈希、身份证号),JWT 是 Base64 编码,非加密
- 前端可通过
jwt_decode(如 js-jwt)直接读取这些字段,无需额外 API 调用
为什么生产环境部署 Nginx 后,Authorization 头突然丢失?
Nginx 默认过滤掉带下划线或特殊字符的 header,Authorization 虽然合法,但在某些旧版配置中会被 proxy_pass 丢弃。
实操修复:
- 在 Nginx 的
location块中显式透传:proxy_set_header Authorization $http_authorization; - 同时补上:
proxy_pass_request_headers on; - 如果用的是 uWSGI,还需确认
uwsgi_pass_request_headers = true - 验证方式:在 view 中打印
request.META.get('HTTP_AUTHORIZATION'),部署前后对比输出
复杂点在于,这个丢失是静默的——Django 根本收不到 header,所以连 401 都不会返回,而是直接走未认证逻辑。最容易被忽略的是没检查 Nginx access log 中的原始请求头。


















