Django自定义中间件导致登录时CSRF令牌验证失败问题
问题根源与修复方案
你的CSRF验证错误完全是因为中间件的执行时机逻辑错误导致的,咱们一步步拆解问题:
为什么原代码会出问题?
你把重定向判断放在了get_response(request)之后——这意味着视图已经完整处理了请求(比如登录的POST请求已经完成,用户状态切换为已登录),之后才触发重定向。这种时序错误会带来两个核心问题:
- 登录请求已经执行完毕,中间件再强制重定向会打乱浏览器的CSRF上下文,直接触发验证失败;
- 逻辑顺序颠倒,本该在视图执行前拦截未完善资料的用户,结果变成了视图执行后才拦截,完全违背了中间件的拦截初衷。
修复后的中间件代码
先调整逻辑顺序,把所有判断移到视图执行前,同时补充排除登录、注册等不需要拦截的路径:
from django.urls import reverse from django.shortcuts import redirect class CompleteProfileMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): # 定义不需要拦截的核心路径:登录、登出、注册 exempt_paths = [ reverse('login'), reverse('logout'), reverse('register'), # 假设你的注册页URL名称是register ] # 仅当用户已登录时,才把完善资料页加入豁免列表 if request.user.is_authenticated: complete_profile_path = reverse( 'complete_registration', # 建议给完善资料页设置清晰的URL名称 kwargs={'username': request.user.username} ) exempt_paths.append(complete_profile_path) # 开始判断拦截逻辑 if request.path in exempt_paths: # 豁免路径,直接处理请求 response = self.get_response(request) elif request.user.is_anonymous or request.user.is_superuser: # 匿名用户或超级管理员,直接放行 response = self.get_response(request) elif not request.user.student.complete_profile: # 未完善资料,直接重定向到完善页面 return redirect(reverse( 'complete_registration', kwargs={'username': request.user.username} )) else: # 已完善资料,正常处理请求 response = self.get_response(request) return response
关键优化点
- 时序调整:所有拦截判断都在
get_response(request)之前执行,确保视图处理前就完成重定向,不会干扰已有的请求流程; - 路径豁免:明确排除登录、注册、登出等核心路径,避免用户在登录过程中被强制重定向;
- 使用URL名称反向解析:用
reverse()代替硬编码路径,后续URL规则变更时无需修改中间件代码; - 健壮性提升:仅在用户已登录时才生成完善资料页的路径,避免匿名用户时出现无效路径。
额外注意事项
- 确保你的
Student模型和User是一对一关联,并且用户注册后已经自动创建了对应的Student实例,避免出现Student.DoesNotExist异常; - 检查你的URL配置,给完善资料页设置正确的名称(比如示例中的
complete_registration),保证reverse()能正确解析路径。
内容的提问来源于stack exchange,提问作者Yash Mittra
相关产品推荐
相关产品推荐

