Django会话认证异常:登录后get_user接口仍返回匿名用户
问题:Django DRF + React 跨域认证后,get_user接口仍返回AnonymousUser
我用Django + Django REST Framework做后端,React做前端开发项目。调用login_user接口确认用户已认证(user_is_authenticated=true)后,调用get_user接口始终返回false,得到AnonymousUser。前端Axios请求已设置withCredentials: true,Chrome开发者工具也确认生成了session_id,但问题依旧。
前端get_user请求代码
useEffect(() => { (async () => { axios .get(url, { withCredentials: true, }) .then((res) => {}); })(); }, [location]);
后端views.py代码
from django.contrib.auth import authenticate, login, logout from django.contrib.auth.hashers import make_password from rest_framework import serializers, status, permissions, status from rest_framework.decorators import api_view, authentication_classes from rest_framework.response import Response from users.serializers import UserSerializer from django.contrib.auth.models import User @api_view(['GET']) def get_user(request): print(request.user.is_authenticated) return Response(status=status.HTTP_200_OK) @api_view(['POST']) def register_user(request): match_username = User.objects.all().filter( username=request.data['username']) match_email = User.objects.all().filter(email=request.data['email']) serializer = UserSerializer(data=request.data) if serializer.is_valid() and not match_username and not match_email: user = User.objects.create(username=serializer.data['username'], password=make_password( serializer.data['password']), email=serializer.data['email']) user.save() send_register_email( serializer.data['username'], serializer.data['email']) return Response(status=status.HTTP_201_CREATED, data=UserSerializer(user).data) else: if match_username: return Response(status=status.HTTP_400_BAD_REQUEST, data='Username already exists') elif match_email: return Response(status=status.HTTP_400_BAD_REQUEST, data='Email already exists') else: return Response(status=status.HTTP_400_BAD_REQUEST, data="Invalid email") @api_view(['POST']) def login_user(request): username = User.objects.all().filter(username=request.data['username']) if not username: return Response(status=status.HTTP_400_BAD_REQUEST, data='Username not found') user = authenticate( username=request.data['username'], password=request.data['password']) if user is not None: login(request, user) return Response(status=status.HTTP_200_OK, data=UserSerializer(user).data) else: return Response(status=status.HTTP_400_BAD_REQUEST, data='Incorrect password')
后端settings.py(相关配置)
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.sites', 'django.contrib.staticfiles', 'rest_framework', 'rest_framework.authtoken', 'corsheaders', 'users', 'settings', ] MIDDLEWARE = [ "corsheaders.middleware.CorsMiddleware", "django.middleware.common.CommonMiddleware", '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', ] REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.SessionAuthentication', ] } PASSWORD_HASHERS = [ "django.contrib.auth.hashers.PBKDF2PasswordHasher", "django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher", "django.contrib.auth.hashers.Argon2PasswordHasher", "django.contrib.auth.hashers.BCryptSHA256PasswordHasher", "django.contrib.auth.hashers.ScryptPasswordHasher", ] ROOT_URLCONF = 'users.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'users.wsgi.application' # CORS配置 CORS_ALLOW_ALL_ORIGINS = True CORS_ALLOW_CREDENTIALS = True
问题排查与解决方案
1. 给get_user视图显式指定认证类
虽然全局配置了SessionAuthentication,但显式给视图指定认证类能确保会话认证生效:
from rest_framework.decorators import authentication_classes from rest_framework.authentication import SessionAuthentication @api_view(['GET']) @authentication_classes([SessionAuthentication]) def get_user(request): print(request.user.is_authenticated) return Response({"is_authenticated": request.user.is_authenticated}, status=status.HTTP_200_OK)
2. 传递CSRF令牌(关键)
跨域场景下,SessionAuthentication需要CSRF令牌验证,否则Django会拒绝会话关联。
前端处理方式:
从Cookie中提取csrftoken,并在请求头中携带:
// 提取CSRF令牌的工具函数 function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } useEffect(() => { (async () => { const csrftoken = getCookie('csrftoken'); axios .get(url, { withCredentials: true, headers: { 'X-CSRFToken': csrftoken } }) .then((res) => { console.log(res.data.is_authenticated); }); })(); }, [location]);
临时排查方案(不推荐生产使用):
给get_user视图豁免CSRF检查,仅用于验证会话是否正常:
from django.views.decorators.csrf import csrf_exempt @api_view(['GET']) @csrf_exempt def get_user(request): print(request.user.is_authenticated) return Response({"is_authenticated": request.user.is_authenticated}, status=status.HTTP_200_OK)
3. 调整Session Cookie属性(本地开发)
如果是本地跨域开发,浏览器可能限制Cookie传递,在settings.py中添加:
SESSION_COOKIE_SAMESITE = 'Lax' # 跨域场景可设为'None',但需配合HTTPS SESSION_COOKIE_SECURE = False # 本地开发设为False,生产环境必须设为True
内容的提问来源于stack exchange,提问作者dice
相关产品推荐
相关产品推荐

