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

React前端对接Django后端JWT用户组认证异常排查

问题:React + Django JWT身份验证异常

我用JWT实现React前端和Django后端的用户(含用户组)身份验证时碰到了问题:

  • Django端写了check_auth视图并配置了路由,直接通过/admin登录后访问/api/check-auth/能正常返回认证信息;
  • React前端调用该接口时,本地存储有有效access/refresh token,Network显示请求带了正确的Authorization头,但始终返回未认证状态(User显示Unknown、Authenticated显示No),而且请求会出现200和302两次响应。

Django端代码

views.py

@csrf_exempt
@login_required
def check_auth(request):
    user = request.user
    response_data = {
        "message": "You are authenticated",
        "user": user.username,
        "authenticated": True,
        "groups": list(user.groups.values_list('name', flat=True))
    }
    return JsonResponse(response_data)

路由配置

  • backend/apples/urls.py:
path('check-auth/', check_auth, name='check_auth'),
  • backend/urls.py:
path("api/", include("apples.urls")),

React端代码

CheckAuth.jsx组件

import React, { useEffect, useState } from 'react';
import api from '../api';

const CheckAuth = () => {
  const [authStatus, setAuthStatus] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchAuthStatus = async () => {
      try {
        const response = await api.get('/api/check-auth/');
        setAuthStatus(response.data);
      } catch (err) {
        setError(err.message);
      }
    };

    fetchAuthStatus();
  }, []);

  if (!authStatus) return <div>Loading...</div>;

  // 处理字段缺失或未定义的情况
  const user = authStatus.user || 'Unknown';
  const authenticated = authStatus.authenticated ? 'Yes' : 'No';
  const groupsDisplay = authStatus.groups ? authStatus.groups.join(', ') : 'No groups';

  return (
    <div>
      <h1>Authentication Status</h1>
      <p>User: {user}</p>
      <p>Authenticated: {authenticated}</p>
      <p>Groups: {groupsDisplay}</p>
    </div>
  );
};

export default CheckAuth;

api.js配置

import axios from 'axios';
import { ACCESS_TOKEN } from './constants';

const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL || 'http://127.0.0.1:8000',
});

axios.defaults.withCredentials = true;

api.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem(ACCESS_TOKEN);
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

export default api;

问题排查与解决方案

1. @login_required不识别JWT Token

@login_required依赖Django的session认证机制,完全忽略Authorization头里的JWT,只会判断当前session是否登录。前端请求没有携带session cookie,所以会被判定为未认证,触发重定向。

解决: 替换为JWT专用的认证装饰器(以djangorestframework-simplejwt为例):

from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework.decorators import authentication_classes, permission_classes
from rest_framework.permissions import IsAuthenticated

@csrf_exempt
@authentication_classes([JWTAuthentication])
@permission_classes([IsAuthenticated])
def check_auth(request):
    user = request.user
    response_data = {
        "message": "You are authenticated",
        "user": user.username,
        "authenticated": True,
        "groups": list(user.groups.values_list('name', flat=True))
    }
    return JsonResponse(response_data)

2. 302重定向问题

@login_required在未通过session认证时,会自动返回302重定向到默认登录页(/accounts/login/),这就是你看到两次响应的原因:第一次请求被重定向,第二次请求登录页返回200,但内容不是接口预期的JSON,导致前端解析出未认证数据。

解决: 除了替换装饰器,也可以自定义认证逻辑,直接返回401而非重定向:

import jwt
from django.conf import settings
from django.contrib.auth.models import User
from django.http import HttpResponseUnauthorized, JsonResponse

@csrf_exempt
def check_auth(request):
    auth_header = request.headers.get('Authorization')
    if not auth_header or not auth_header.startswith('Bearer '):
        return HttpResponseUnauthorized(
            JsonResponse({"authenticated": False, "user": "Unknown"}).content,
            content_type='application/json'
        )
    
    token = auth_header.split(' ')[1]
    try:
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=['HS256'])
        user = User.objects.get(id=payload['user_id'])
    except (jwt.InvalidTokenError, User.DoesNotExist):
        return HttpResponseUnauthorized(
            JsonResponse({"authenticated": False, "user": "Unknown"}).content,
            content_type='application/json'
        )
    
    response_data = {
        "message": "You are authenticated",
        "user": user.username,
        "authenticated": True,
        "groups": list(user.groups.values_list('name', flat=True))
    }
    return JsonResponse(response_data)

3. Axios实例配置问题

你设置了全局axios.defaults.withCredentials = true,但实际使用的是axios.create()创建的独立实例,全局配置不会自动应用到实例上。

解决: 把withCredentials移到实例配置中:

const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL || 'http://127.0.0.1:8000',
  withCredentials: true, // 移到此处
});

// 移除全局的 axios.defaults.withCredentials = true;

4. CORS配置问题

确保Django的CORS配置允许携带Authorization头和凭证,使用django-cors-headers的话,在settings.py中添加:

CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_HEADERS = [
    'authorization',
    'content-type',
]

内容的提问来源于stack exchange,提问作者George

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 22:31:09