使用DRF API登录后重定向至Django视图失败问题排查
问题:DRF登录API返回200,但跳转
@login_required视图时被重定向到登录页 使用Django REST Framework实现的登录API返回200状态码,但尝试跳转至带有@login_required装饰器的dashboard视图时,系统返回302状态码并自动跳转到登录页面。
控制台日志
[09/Aug/2024 18:17:04] "GET /static/js/login.js HTTP/1.1" 200 2297 [09/Aug/2024 18:17:24] "POST /api/v1/token/login HTTP/1.1" 200 70 [09/Aug/2024 18:17:25] "GET /dashboard/ HTTP/1.1" 302 0 [09/Aug/2024 18:17:25] "GET /accounts/login/?next=/dashboard/ HTTP/1.1" 200 1451 [09/Aug/2024 18:17:25] "GET /static/js/login.js HTTP/1.1" 304 0 [09/Aug/2024 18:18:46] "GET /accounts/login/?next=/dashboard/ HTTP/1.1" 200 1451
登录API代码
import json from rest_framework.decorators import api_view,permission_classes from rest_framework.response import Response from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework import status from core.models import Order, Laundry from .serializers import OrderSerializer, SignUpSerializer from django.views.decorators.csrf import ensure_csrf_cookie,csrf_exempt, csrf_protect from rest_framework.authtoken.models import Token from django.contrib.auth import authenticate,logout, login,get_user_model @api_view(['POST']) @permission_classes([AllowAny]) @ensure_csrf_cookie def user_login_token(request): """user login api""" if request.method == 'POST': email = request.data.get('Email') password = request.data.get('Password') current_user = authenticate(request, username=email, password=password) if current_user: login(request, current_user) token, created = Token.objects.get_or_create(user=current_user) response = Response({'token': token.key, 'SuperRole': current_user.is_superuser}, status=status.HTTP_200_OK) return response return Response({'error': 'Invalid credentials'}, status=status.HTTP_400_BAD_REQUEST)
前端表单提交代码
const loginForm = document.querySelector('form'); loginForm.addEventListener('submit', async(event) => { event.preventDefault(); const email = document.getElementById('email').value; const password = document.getElementById('password').value; const csrfToken = document.querySelector('meta[name="csrf-token"]').content; try { const response = await fetch('http://127.0.0.1:8000/api/v1/token/login', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken, }, body: JSON.stringify({ Email: email, Password: password }), credentials: 'include' }); const data = await response.json(); if (response.ok) { const token = data.token; localStorage.setItem('token', token); if (data.SuperRole) { console.log("administrator login"); } else { window.location.href = '/dashboard/'; } } else { console.error('Error:', data); } } catch (error) { console.error('Error:', error); } }); 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; }
Dashboard视图代码
@login_required def user_dashboard(request): return render(request, "user_dashboard.html")
原因及解决方法
核心原因
Django的@login_required装饰器依赖**会话Cookie(session cookie)**验证用户登录状态,但当前流程中,前端跳转/dashboard/时未携带有效的会话凭证,导致Django判定用户未登录。
虽然API中调用了login(request, current_user),但前端fetch请求的会话Cookie可能因配置问题未被正确保存或携带,或是存在跨域场景导致Cookie无法传递。
具体修复步骤
修正Django会话Cookie配置
在settings.py中调整以下配置:# 本地开发时关闭Secure(生产环境需开启) SESSION_COOKIE_SECURE = False # 允许跨域或同站请求携带Cookie SESSION_COOKIE_SAMESITE = 'Lax' # 本地开发留空或设为'127.0.0.1' SESSION_COOKIE_DOMAIN = None确保API正确保存会话
在登录API的login调用后,显式保存会话:if current_user: login(request, current_user) # 新增:强制保存会话,确保Cookie被设置 request.session.save() token, created = Token.objects.get_or_create(user=current_user) response = Response({'token': token.key, 'SuperRole': current_user.is_superuser}, status=status.HTTP_200_OK) return response统一请求地址避免跨域
前端fetch请求使用相对路径,避免绝对地址导致的跨域问题:// 把绝对地址改为相对路径 const response = await fetch('/api/v1/token/login', { // ...其他配置不变 });Token认证适配方案(可选)
如果要保留Token认证方式,需修改Dashboard视图的验证逻辑,让它支持Token:from rest_framework.authentication import TokenAuthentication from rest_framework.decorators import authentication_classes, permission_classes from rest_framework.permissions import IsAuthenticated @authentication_classes([TokenAuthentication]) @permission_classes([IsAuthenticated]) def user_dashboard(request): return render(request, "user_dashboard.html")注意:这种方式下,前端跳转时需要通过拦截器在请求头中携带
Authorization: Token <token>,更适合纯API场景;若使用模板渲染视图,优先修复会话Cookie问题更简便。
内容的提问来源于stack exchange,提问作者Aqib Iliyasu Abdullahi
相关产品推荐
相关产品推荐

