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

使用rest_framework_simplejwt时自定义JWT加密中间件不生效排查

问题分析与解决方案

核心问题:中间件未正确修改响应内容

你的中间件逻辑本身没问题,但忽略了DRF Response对象的渲染缓存机制——当Response的content属性被访问过(比如某个上游中间件提前渲染了响应),后续修改response.data不会自动更新返回给客户端的内容,导致明文token依然被返回。

修复步骤

1. 让中间件修改后重新渲染响应

在删除response.data中的access和refresh后,添加代码清空缓存并重新渲染:

# 清空已缓存的响应内容,触发重新渲染
response.content = None
response.render()

修改后的中间件关键代码段:

del response.data["access"]
del response.data["refresh"]

# 新增:清空缓存并重新渲染
response.content = None
response.render()

response.set_cookie(
    "access_token",
    encrypted_access_token,
    httponly=True,
    secure=True,
    samesite="Strict",
)
response.set_cookie(
    "refresh_token",
    encrypted_refresh_token,
    httponly=True,
    secure=True,
    samesite="Strict",
)

2. 验证中间件是否被正确触发

在process_response方法中添加打印日志,确认代码是否进入了token处理分支:

def process_response(self, request, response):
    print("Token加密中间件已调用")
    if response.status_code == 200 and hasattr(response, 'data'):
        print("检测到DRF Response对象")
        if "access" in response.data and "refresh" in response.data:
            print("开始处理JWT令牌")
            # 你的加密逻辑...
    return response

如果没有打印对应日志,检查MIDDLEWARE配置中的中间件路径是否正确。

3. 更可靠的方案:自定义Token视图替代中间件

中间件会处理所有HTTP响应,容易误触发。更精准的方式是重写TokenObtainPairView,直接在登录视图中处理加密和Cookie设置:

from rest_framework_simplejwt.views import TokenObtainPairView
from cryptography.fernet import Fernet
from django.conf import settings
from rest_framework.response import Response

class CustomTokenObtainPairView(TokenObtainPairView):
    def post(self, request, *args, **kwargs):
        response = super().post(request, *args, **kwargs)
        if response.status_code == 200:
            # 用合法Fernet密钥初始化加密器
            cipher = Fernet(settings.JWT_KEY.encode())
            
            encrypted_access = cipher.encrypt(response.data["access"].encode()).decode()
            encrypted_refresh = cipher.encrypt(response.data["refresh"].encode()).decode()

            # 设置Cookie,适配开发/生产环境
            response.set_cookie(
                "access_token",
                encrypted_access,
                httponly=True,
                secure=not settings.DEBUG,
                samesite="Strict",
            )
            response.set_cookie(
                "refresh_token",
                encrypted_refresh,
                httponly=True,
                secure=not settings.DEBUG,
                samesite="Strict",
            )
            # 清空响应数据,不返回明文token
            response.data = {}
            response.content = None
            response.render()
        return response

然后在urls.py中替换默认路由:

from django.urls import path
from .views import CustomTokenObtainPairView

urlpatterns = [
    path('token/', CustomTokenObtainPairView.as_view(), name='token_obtain_pair'),
    # 其他路由
]

加密逻辑的修正

你当前的密钥处理方式存在错误:Fernet要求密钥是urlsafe base64编码的32字节原始密钥。正确的做法是:

  1. 在Django Shell中生成合法密钥:
from cryptography.fernet import Fernet
print(Fernet.generate_key().decode())
  1. 将生成的字符串存入settings.py:
JWT_KEY = "生成的Fernet密钥字符串"
  1. 在代码中直接使用:
cipher = Fernet(settings.JWT_KEY.encode())

rest_framework_simplejwt与自定义中间件的交互注意事项

  • 响应渲染时机:DRF Response的content属性被访问后会缓存渲染结果,修改data后必须清空缓存并重新渲染,否则修改不会生效。
  • 中间件范围控制:中间件会拦截所有响应,必须通过精准的条件判断(比如检查请求路径是否为token接口)避免误处理其他响应。
  • Token视图的默认行为:TokenObtainPairView默认返回明文token,必须确保在响应发送前清空这些字段,无论是在视图还是中间件中。
  • Cookie属性适配环境:secure=True仅在HTTPS环境下有效,开发环境建议用not settings.DEBUG动态设置,否则Cookie无法被浏览器保存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 21:15:01