Django会话过期后,自定义login_required装饰器如何返回401响应?
问题
我编写了如下自定义login_required装饰器:
def login_required(function): """ Ensure that user is logged in """ def wrap(request, *args, **kwargs): if request.user.is_anonymous: message = { "status": "error", "message": "user login error", "data": "user is not logged in", } return HttpResponse( content=json.dumps(message).encode("utf-8"), status=401, headers=None, content_type="application/json", ) return function(request, *args, **kwargs) return wrap
当请求到来且Django会话过期时,我能在响应中看到自定义消息,但响应状态码为302,会重定向到带有/accounts/login/?next=的URL。
我已在settings中配置了SESSION_TIMEOUT_REDIRECT、LOGOUT_REDIRECT、LOGIN_URL,尝试过调整不同Django设置但未解决问题。
当前使用的中间件如下:
MIDDLEWARE = [ "csp.middleware.CSPMiddleware", "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django_session_timeout.middleware.SessionTimeoutMiddleware", "corsheaders.middleware.CorsMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "whitenoise.middleware.WhiteNoiseMiddleware", "core.middleware.admin_pages_middleware.AdminPagesMiddleware", ]
技术栈:
- Python 3.12
- Django 5.0
我希望响应返回401而非302,请问我遗漏了什么配置?
解决方案
1. 修正django_session_timeout中间件配置
你使用的django_session_timeout.middleware.SessionTimeoutMiddleware优先级高于自定义装饰器,会话过期时会自动触发重定向到LOGIN_URL,覆盖了你返回401的逻辑。
在settings.py中设置:
SESSION_TIMEOUT_REDIRECT = None
该配置会让中间件在会话过期时不主动重定向,交由你的装饰器处理响应。
2. 避免与Django自带login_required混用
确认你的视图仅使用自定义的login_required装饰器,不要和Django自带的django.contrib.auth.decorators.login_required混用——自带装饰器默认会对未登录请求返回302重定向。
3. 强制清除重定向响应头
在自定义装饰器中,添加逻辑清除可能触发重定向的Location头,确保401响应不被篡改:
def login_required(function): """ Ensure that user is logged in """ def wrap(request, *args, **kwargs): if request.user.is_anonymous: message = { "status": "error", "message": "user login error", "data": "user is not logged in", } response = HttpResponse( content=json.dumps(message).encode("utf-8"), status=401, content_type="application/json", ) # 清除可能存在的重定向头 response.pop('Location', None) return response return function(request, *args, **kwargs) return wrap
4. 检查自定义中间件AdminPagesMiddleware
排查core.middleware.admin_pages_middleware.AdminPagesMiddleware的代码,确认该中间件没有拦截未登录请求并触发重定向的逻辑——如果存在相关逻辑,需要调整其优先级或修改判断条件,避免覆盖401响应。
内容的提问来源于stack exchange,提问作者Vroom

