django-allauth无头模式下X-Session-Token有效但request.user未认证
问题分析与解决
核心问题在于:django-allauth无头模式的SessionTokenMiddleware默认仅处理/_allauth/前缀的请求,你的自定义API路径不在其处理范围内,导致即使正确传入X-Session-Token,request.user也无法被自动填充。而ninja.security.django_auth依赖Django默认的Session Cookie认证机制,和allauth无头的Token认证逻辑不兼容,因此会返回权限拒绝。
解决方案一:让allauth中间件覆盖自定义API路径
在项目settings.py中添加ALLAUTH_HEADLESS配置,将你的API路径纳入中间件处理范围:
ALLAUTH_HEADLESS = { "SESSION_TOKEN_MIDDLEWARE_URLS": [ r"^/_allauth/", r"^/api/", # 新增API路径匹配规则 ], }
同时确保allauth.headless.middleware.SessionTokenMiddleware已正确加入MIDDLEWARE列表,且顺序符合要求(在SessionMiddleware之后、AuthenticationMiddleware之前):
MIDDLEWARE = [ # ... 其他中间件 "django.contrib.sessions.middleware.SessionMiddleware", "allauth.headless.middleware.SessionTokenMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", # ... 其他中间件 ]
配置完成后,allauth中间件会自动处理/api/开头的请求,通过X-Session-Token完成认证并填充request.user,此时使用ninja.security.django_auth即可正常生效。
解决方案二:自定义Ninja认证类(灵活适配)
若不想让allauth中间件处理所有API请求,可自定义适配allauth Session Token的Ninja认证类:
- 创建
security.py文件编写认证逻辑:
from ninja.security import HttpBearer from allauth.headless.internal.session import get_session_from_token from django.contrib.auth import get_user_model from django.contrib.auth.models import AnonymousUser User = get_user_model() class AllAuthSessionTokenAuth(HttpBearer): def authenticate(self, request, token): # 通过Token获取对应Session session = get_session_from_token(token) if not session: return AnonymousUser() # 从Session中提取用户ID并返回用户实例 user_id = session.get("_auth_user_id") if user_id: try: return User.objects.get(pk=user_id) except User.DoesNotExist: pass return AnonymousUser()
- 在Ninja API或路由中使用该认证类:
# api.py from ninja import NinjaAPI from .security import AllAuthSessionTokenAuth api = NinjaAPI(auth=AllAuthSessionTokenAuth()) api.add_router("website/", "website.api.router")
或针对单个路由配置:
# website.api.py from ninja import Router from .security import AllAuthSessionTokenAuth # ... 其他导入 router = Router(auth=AllAuthSessionTokenAuth()) @router.get("/blog_category", response=List[BlogCategoryRetrieveSchema]) async def blog_category_retrieve(request): print(request.user) # 此时会输出已认证的用户实例 # ... 业务逻辑
两种方案均可解决问题:方案一更简洁,适合全API需要allauth认证的场景;方案二更灵活,可针对特定路由或API配置认证规则。
内容的提问来源于stack exchange,提问作者Coding Guy
相关产品推荐
相关产品推荐

