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
相关产品推荐
相关产品推荐

