You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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无法传递。

具体修复步骤

  1. 修正Django会话Cookie配置
    在settings.py中调整以下配置:

    # 本地开发时关闭Secure(生产环境需开启)
    SESSION_COOKIE_SECURE = False
    # 允许跨域或同站请求携带Cookie
    SESSION_COOKIE_SAMESITE = 'Lax'
    # 本地开发留空或设为'127.0.0.1'
    SESSION_COOKIE_DOMAIN = None
    
  2. 确保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
    
  3. 统一请求地址避免跨域
    前端fetch请求使用相对路径,避免绝对地址导致的跨域问题:

    // 把绝对地址改为相对路径
    const response = await fetch('/api/v1/token/login', {
        // ...其他配置不变
    });
    
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 17:05:57