Django URL重定向后request.user重置为AnonymousUser问题排查
异常产生原因
- 中间件注册顺序错误:Django中间件按配置列表从上到下执行
process_request逻辑,django.contrib.auth.middleware.AuthenticationMiddleware负责从session中解析用户身份、给request对象绑定user属性。如果自定义的AuthRequiredMiddleware配置在AuthenticationMiddleware之前,执行认证校验时AuthenticationMiddleware还未完成用户解析,拿到的request.user是默认的匿名用户。该问题在静态路由下未暴露,多是因为静态路由命中了框架或服务器的缓存逻辑,未走到自定义中间件的校验分支,配置动态路由后缓存失效触发异常。 - 路径匹配逻辑缺陷:现有代码用精确字符串相等判断路径是否在豁免列表,完全无法适配带参数的动态路径,也无法正确匹配静态资源、媒体资源等通配路径,容易出现非预期的重定向。比如动态页面中用相对路径引用的资源会被误拦截,反复重定向到登录页的过程中会触发session重置,最终导致已登录用户被识别为匿名用户。
- 全局缓存逻辑有并发安全问题:代码中用固定key
_cached_user在进程全局缓存中存储用户对象,多用户并发请求时会出现缓存值互相覆盖的问题,一旦缓存中存入匿名用户或缓存失效,后续请求读取该缓存时就会拿到错误的用户身份。
修复方案
- 调整
settings.py中MIDDLEWARE列表的顺序,确保自定义认证中间件在SessionMiddleware、AuthenticationMiddleware之后,参考顺序如下:
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', # 自定义认证中间件放在上述系统中间件之后 'yourapp.middleware.AuthRequiredMiddleware', ]
- 替换精确字符串匹配的路径判断逻辑,改用正则匹配支持动态路径、通配路径,同时把静态资源、媒体资源路径加入豁免列表,修复后的中间件代码参考:
from django.utils.deprecation import MiddlewareMixin from django.shortcuts import redirect from django.conf import settings import re EXEMPT_URLS = [] # 加入登录页豁免规则 if settings.LOGIN_URL: EXEMPT_URLS.append(re.compile(r'^%s$' % settings.LOGIN_URL.lstrip('/'))) # 加入配置的自定义豁免路径规则 if hasattr(settings, 'LOGIN_EXEMPT_URLS'): for url in settings.LOGIN_EXEMPT_URLS: EXEMPT_URLS.append(re.compile(r'^%s$' % url.lstrip('/'))) # 加入静态资源、媒体资源豁免规则 if hasattr(settings, 'STATIC_URL') and settings.STATIC_URL: EXEMPT_URLS.append(re.compile(r'^%s.*$' % settings.STATIC_URL.lstrip('/'))) if hasattr(settings, 'MEDIA_URL') and settings.MEDIA_URL: EXEMPT_URLS.append(re.compile(r'^%s.*$' % settings.MEDIA_URL.lstrip('/'))) class AuthRequiredMiddleware(MiddlewareMixin): def process_request(self, request): path = request.path_info.lstrip('/') # 命中豁免路径直接放行 if any(url.match(path) for url in EXEMPT_URLS): return # 未登录用户重定向到登录页 if not request.user.is_authenticated: return redirect('login')
- 删除原代码中全局缓存存储用户的逻辑:
request.user已经由Django自带的AuthenticationMiddleware完成解析和绑定,不需要额外存储在全局缓存中,固定key的全局缓存本身存在多用户串号的安全隐患,必须移除。
开发注意:模板中引用静态资源、接口地址时尽量用绝对路径,配合Django自带的
{% static %}、{% url %}标签生成地址,避免相对路径被动态路由误解析,触发不必要的拦截。
内容的提问来源于stack exchange,提问作者Alexander
相关产品推荐
相关产品推荐

